1 项目需求
使用MSPM0L1306的定时器,生成2路重复频率1Hz~1MHz的PWM信号,每一路的重复频率和占空比都可独立调节。
其中,PWM重复频率在1Hz~10KHz内连续可调,通过按键切换至100KHz和1MHz。
PWM重复频率越高,占空比分辨率降低(主时钟32MHz为例)PWM重复频率在1Hz~10KHz时,占空比的精度不低于1/1000,100Khz和1Mhz的占空比精度按照表格即可。
使用按键或拨码开关组合调节输出频率、占空比,并由按键控制每一路PWM信号的输出。
能在OLED屏幕上显示基础信息,当前使用引脚示意、引脚相应的PWM参数。
将PWM参数(包括两个通道的频率占空比、及通道工作与否)按照1s左右的周期,通过串口发送到上位机
2 完成的功能及达到的性能
2.1 双通道信息显示
如图,使用单片机上的两个GPIO作为输出,其中通道一为PA26,通道2为PA22,两个通道频率和占空比均可调且显示在OLED上面。其中频率从1-32000可连续调节,单位为1Hz,占空比按照表格要求,精度在万分之一。通过按键可切换100K和1MHz,占空比同样可调。两个通道频率可分别设置100K和1MHz,
2.2双通道UART信息发送
程序每隔约一秒的时间向上位机发送PWM参数(包括两个通道的频率占空比、及通道工作与否)
2.3双通道频率可调、占空比可调、通道选择
通过4*4键盘,其中按键S1,S5,S9,S2,S6,S10,S3,S7,S11分别代表按键1-9,S8表示数字0,S13为通道切换按钮,S14为100K和1MHz按钮,S15为选择调频还是调节占空比,S16为当前输入数字删除(退格),S4为开始输入按钮,S12为结束输入按钮。
2.4 操作步骤
上图为开始界面,按下S4,则显示开始输入,默认调节通道1频率。通过九键输入1-32000范围内的数字,确认后按下S12,显示确认信息,观察引脚输出。
如果需要切换通道,按下S13,显示切换后的通道信息,通过九键输入1-32000范围内的数字,确认后按下S12,显示确认信息,观察引脚输出
如果需要调节占空比,按下S15,显示调节占空比模式的信息,通过九键输入四位数,默认输入的是0-1的范围内的小数部分,确认后按下S12,显示确认信息,观察引脚输出。
如果需要调节频率为100K或1MHz,默认频率单位为Hz,按下S14切换为100KHz,此时对应通道频率被修改为100KHz,按下确认键S12,观察引脚输出。如果需要修改占空比,则同上。
再次按下S14,切换为1MHz,此时对应通道频率被修改为1MHz,按下确认键S12,观察引脚输出。如果需要修改占空比,则同上。
3 设计思路
· 首先实现矩阵键盘数据的读取,并且实现实时显示输入的数据
· 矩阵键盘的功能包括五位数输入,退格,实时显示
· 按下确认键后,输入的值会根据当前模式和通道赋给对应的变量,然后更新PWM波的参数
· 配置PWM波需要三个参数即时钟配置、PWM周期配置和比较值配置。其中时钟配置决定了定时器的时钟,也间接限制了PWM波的频率和精度,PWM周期配置则是需要根据输入的频率值计算计数器值,从而实现频率可调。比较值则是调节占空比的参数,在已知PWM波周期值的情况下,通过修改比较值确定引脚翻转的时间,从而实现占空比可调。
4 实现过程
4.1 程序流程图
4.2 按键输入数据及模式切换
矩阵按键扫描的代码如下
在这部分代码中,我沿用了例程中的代码,这个代码实现了一次性扫描16个按键,并且通过延时实现了按键消抖。每次只扫一行四个按键,重复执行四次实现了16个按键的扫描
uint8_t Key()
{
uint8_t key_num = 0; // 按键值1-16,默认为0
static uint8_t key_flag = 0; // 按下按键标志
if (key_flag)
{
delay_ms(300); // 300ms延迟,防止按下一次按键却被认为按下了多次按键,导致得到了多个相同的按键值
key_flag = 0; // 按下按键标志清零
}
// 行扫描
// ROW 0111
// 0111-1011-1101-1110
DL_GPIO_clearPins(MAT_KEY_PORT, MAT_KEY_ROW0_PIN);
DL_GPIO_setPins(MAT_KEY_PORT, MAT_KEY_ROW1_PIN | MAT_KEY_ROW2_PIN | MAT_KEY_ROW3_PIN);
delay_ms(10); // 按键消抖延迟,一般为10ms,一定要在外界改变时延迟,而不是读取输入后再延迟
// 所谓的外界改变时,即改变行扫描时,如行输出由0111变为1011
// 这里实际上有两个外界改变时,一个是用户刚刚按下时,另一个是改变行扫描时
// 然而由于按键消抖延迟只需10ms,扫描一轮也只需40ms,远小于用户按下的时间
// 因此选择改变行扫描时作为外界改变时,总能涵盖两种外界改变
// 并且在按键消抖例程中提到,无法得知用户刚刚按下的时刻
// 同时不断判断低电平也不能近似这个时刻,因为低电平出现有两种情况
// 一种是用户先按下,然后改变行扫描,另一种是先改变行扫描,用户再按下
// 普遍错误写法如下:
// DL_GPIO_clearPins(MAT_KEY_PORT, MAT_KEY_ROW0_PIN);
// DL_GPIO_setPins(MAT_KEY_PORT, MAT_KEY_ROW1_PIN | MAT_KEY_ROW2_PIN | MAT_KEY_ROW3_PIN);
// if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL0_PIN))//每次改变行扫描后,直接读取并未经过延迟,有可能只能读到抖动中的高电平,从而永远检测不到,如常见的第二行“不灵敏”问题,究其原因是134行过于灵敏导致消抖错误也能检测正确
// {
// delay_ms(10);
// if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL0_PIN))
// {
// key_num = 1;
// key_flag = 1;
// }
// }
if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL0_PIN))
{
key_num = 1;
key_flag = 1; // 按键已按下
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL1_PIN))
{
key_num = 2;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL2_PIN))
{
key_num = 3;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL3_PIN))
{
key_num = 4;
key_flag = 1;
}
// ROW 1011
DL_GPIO_clearPins(MAT_KEY_PORT, MAT_KEY_ROW1_PIN);
DL_GPIO_setPins(MAT_KEY_PORT, MAT_KEY_ROW0_PIN | MAT_KEY_ROW2_PIN | MAT_KEY_ROW3_PIN);
delay_ms(10);
if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL0_PIN))
{
key_num = 5;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL1_PIN))
{
key_num = 6;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL2_PIN))
{
key_num = 7;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL3_PIN))
{
key_num = 8;
key_flag = 1;
}
// ROW 1101
DL_GPIO_clearPins(MAT_KEY_PORT, MAT_KEY_ROW2_PIN);
DL_GPIO_setPins(MAT_KEY_PORT, MAT_KEY_ROW0_PIN | MAT_KEY_ROW1_PIN | MAT_KEY_ROW3_PIN);
delay_ms(10);
if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL0_PIN))
{
key_num = 9;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL1_PIN))
{
key_num = 10;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL2_PIN))
{
key_num = 11;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL3_PIN))
{
key_num = 12;
key_flag = 1;
}
DL_GPIO_clearPins(MAT_KEY_PORT, MAT_KEY_ROW3_PIN);
DL_GPIO_setPins(MAT_KEY_PORT, MAT_KEY_ROW0_PIN | MAT_KEY_ROW1_PIN | MAT_KEY_ROW2_PIN);
delay_ms(10);
// ROW 1110
if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL0_PIN))
{
key_num = 13;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL1_PIN))
{
key_num = 14;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL2_PIN))
{
key_num = 15;
key_flag = 1;
}
else if (!DL_GPIO_readPins(MAT_KEY_PORT, MAT_KEY_COL3_PIN))
{
key_num = 16;
key_flag = 1;
}
return key_num;
}
各个按键对应选择执行的代码如下:
在这个代码中,主要是根据读取的键值执行对应数字字符的输入,特殊的在于13、14、15、16、4、12这些按键对应的功能会有略微区别。尤其需要说明的是如果在设置PWM波时未确定已键入值,也就是没有按下S12就按下了切换模式或者通道或频率的按键,虽然屏幕会显示已键入值,但是PWM波并不会改变配置,这是一个防误触的措施,避免输出用户不想要的PWM波。
switch (key_num)
{
case 1:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('1');
UpdateDisplay();
}
break;
case 2:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('2');
UpdateDisplay();
}
break;
case 3:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('3');
UpdateDisplay();
}
break;
case 4:
ChangeChannel();
if (inputState == INPUT_ACTIVE)
{
UpdateDisplay();
}
else
{
OLED_ShowString(10, 3, "Please Start!");
}
break;
case 5:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('4');
UpdateDisplay();
}
break;
case 6:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('5');
UpdateDisplay();
}
break;
case 7:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('6');
UpdateDisplay();
}
break;
case 8:
ChangeFrequencyUnit();
if (inputState == INPUT_ACTIVE)
{
UpdateDisplay();
}
else
{
OLED_ShowString(10, 3, "Please Start!");
}
break;
case 9:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('7');
UpdateDisplay();
}
break;
case 10:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('8');
UpdateDisplay();
}
break;
case 11:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('9');
UpdateDisplay();
}
break;
case 12:
ChangeMode();
if (inputState == INPUT_ACTIVE)
{
UpdateDisplay();
}
else
{
OLED_ShowString(10, 3, "Please Start!");
}
break;
case 13:
OLED_Clear();
OLED_ShowString(20, 3, "Start Input!");
delay_ms(500);
StartInput();
UpdateDisplay();
break;
case 14:
if (inputState == INPUT_ACTIVE)
{
Value = InputNumber('0');
UpdateDisplay();
}
break;
case 15:
Value = EndInput();
OLED_Clear();
OLED_ShowString(30, 3, "Confirm!");
delay_ms(500);
UpdateDisplay();
if (freqUnit == FREQUENCY_100KHZ)
{
if(pwmChannel == CHANNEL_1)
{
PWM1_Freq = 100000;
}
else if(pwmChannel == CHANNEL_2)
{
PWM2_Freq = 100000;
}
}
else if (freqUnit == FREQUENCY_1MHZ)
{
if(pwmChannel == CHANNEL_1)
{
PWM1_Freq = 1000000;
}
else if(pwmChannel == CHANNEL_2)
{
PWM2_Freq = 1000000;
}
}
UpdatePWMSettings();
break;
case 16:
if (inputState == INPUT_ACTIVE || inputState == INPUT_OVERFLOW)
{
Value = DeleteLastInput();
UpdateDisplay();
}
break;
default:
break;
}
各按键键入值的功能函数如下:
在这个代码中,主要包括开始输入、输入值、结束输入以及三个切换按键的函数和删除一位数字的函数。在这里我们使用枚举类型来表示当前的输入状态、输入模式、频率单位。当用户开始输入时,按下九键才会把对应的字符放入数组,每次放入后将当前数组的内容通过字符串转换函数转为uint32t类型的值并返回。输入代码是往数组加一位、删除函数则是数组内容减少一位。
#include "NumberInput.h"
InputState inputState = INPUT_IDLE;
PWMMode currentMode = MODE_FREQUENCY;
PWMChannel pwmChannel = CHANNEL_1;
FrequencyUnit freqUnit = FREQUENCY_HZ; // 初始状态为赫兹
uint32_t NumberIndex = 0;
char Number[MAX_DIGITS+1] = {0};
void StartInput() {
inputState = INPUT_ACTIVE;
NumberIndex = 0;
memset(Number, 0, sizeof(Number));
}
uint32_t EndInput() {
uint32_t inputValue = 0;
inputState = INPUT_CONFIRMED;
inputValue = (uint32_t)strtoul(Number, NULL, 10);
memset(Number, 0, sizeof(Number));
NumberIndex = 0;
return inputValue;
}
uint32_t InputNumber(char key)
{
uint32_t currentValue = 0;
if (inputState == INPUT_ACTIVE)
{
if (NumberIndex < MAX_DIGITS)
{
Number[NumberIndex] = key;
NumberIndex++;
Number[NumberIndex] = '\0'; // 确保字符串以'\0'结尾
currentValue = (uint32_t)strtoul(Number, NULL, 10);
if(currentValue>32000)
{
currentValue=DeleteLastInput();
OLED_Clear();
OLED_ShowString(30, 3, "Overflow!");
delay_ms(500);
OLED_Clear();
currentValue = (uint32_t)strtoul(Number, NULL, 10);
inputState = INPUT_OVERFLOW;
}
}
else
{
OLED_Clear();
OLED_ShowString(30, 3, "Overflow!");
delay_ms(500);
OLED_Clear();
currentValue = (uint32_t)strtoul(Number, NULL, 10);
inputState = INPUT_OVERFLOW;
}
}
return currentValue;
}
void ChangeMode()
{
uint32_t value = EndInput();
currentMode = (currentMode == MODE_FREQUENCY) ? MODE_DUTY_CYCLE : MODE_FREQUENCY;
OLED_Clear();
if(currentMode==MODE_DUTY_CYCLE)
{
OLED_ShowString(30, 3, "Set Duty");
}
else if(currentMode==MODE_FREQUENCY)
{
OLED_ShowString(10, 3, "Set Frequency");
}
delay_ms(500);
OLED_Clear();
// StartInput();
}
void ChangeChannel()
{
uint32_t value = EndInput();
pwmChannel = (pwmChannel == CHANNEL_1) ? CHANNEL_2 : CHANNEL_1;
OLED_Clear();
if(pwmChannel==CHANNEL_1)
{
OLED_ShowString(30, 3, "Channel_1");
}
else if(pwmChannel==CHANNEL_2)
{
OLED_ShowString(30, 3, "Channel_2");
}
delay_ms(500);
OLED_Clear();
// StartInput();
}
void ChangeFrequencyUnit()
{
uint32_t value = EndInput();
freqUnit = (freqUnit + 1) % 3;
OLED_Clear();
if(freqUnit == FREQUENCY_100KHZ) {
OLED_ShowString(50, 3, "100KHz");
}
else if(freqUnit == FREQUENCY_1MHZ) {
OLED_ShowString(50, 3, "1MHz");
}
else if(freqUnit == FREQUENCY_HZ) {
OLED_ShowString(60, 3, "Hz");
}
delay_ms(500);
OLED_Clear();
if(freqUnit == FREQUENCY_100KHZ || freqUnit == FREQUENCY_1MHZ)
{
StartInput();
}
}
uint32_t DeleteLastInput()
{
if (NumberIndex > 0)
{
NumberIndex--;
Number[NumberIndex] = '\0'; // 删除最后一位输入,并保证字符串以'\0'结尾
inputState = INPUT_ACTIVE;
}
uint32_t currentValue = (uint32_t)strtoul(Number, NULL, 10); // 获取当前值
return currentValue;
}
PWM波设置函数如下:(仅展示部分)
这里根据要调节的频率及占空比设置对应的时钟配置和PWM配置,相当于重新配置一次PWM波的配置。这里为了分别独立配置两个通道,因此使用了两个定时器配置。
void UpdatePWMSettings()
{
uint32_t Freq=0;
double Duty=0;
if(pwmChannel==CHANNEL_1)
{
Duty=PWM1_Duty;
Freq=PWM1_Freq;
if(Freq==100000)
{
period1=320;
Comparvalue1=(uint32_t)round_num(320*(1-Duty));
DL_TimerG_setClockConfig(PWM_0_INST, (DL_TimerG_ClockConfig *) &gPWM_ClockConfig_100K);
DL_TimerG_initPWMMode(PWM_0_INST, (DL_TimerG_PWMConfig *) &gPWM_Config_100K);
DL_TimerG_setCaptureCompareValue(PWM_0_INST, Comparvalue1, DL_TIMER_CC_0_INDEX);
DL_TimerG_setCaptureCompareOutCtl(PWM_0_INST, DL_TIMER_CC_OCTL_INIT_VAL_LOW,
DL_TIMER_CC_OCTL_INV_OUT_DISABLED, DL_TIMER_CC_OCTL_SRC_FUNCVAL,
DL_TIMERG_CAPTURE_COMPARE_0_INDEX);
DL_TimerG_setCaptCompUpdateMethod(PWM_0_INST, DL_TIMER_CC_UPDATE_METHOD_IMMEDIATE, DL_TIMERG_CAPTURE_COMPARE_0_INDEX);
DL_TimerG_enableClock(PWM_0_INST);
DL_TimerG_setCCPDirection(PWM_0_INST , DL_TIMER_CC0_OUTPUT );
}
else if(Freq==1000000)
{
period1=32;
Comparvalue1=(uint32_t)round_num(32*(1-Duty));
DL_TimerG_setClockConfig(PWM_0_INST, (DL_TimerG_ClockConfig *) &gPWM_ClockConfig_1M);
DL_TimerG_initPWMMode(PWM_0_INST, (DL_TimerG_PWMConfig *) &gPWM_Config_1M);
DL_TimerG_setCaptureCompareValue(PWM_0_INST, Comparvalue1, DL_TIMER_CC_0_INDEX);
DL_TimerG_setCaptureCompareOutCtl(PWM_0_INST, DL_TIMER_CC_OCTL_INIT_VAL_LOW,
DL_TIMER_CC_OCTL_INV_OUT_DISABLED, DL_TIMER_CC_OCTL_SRC_FUNCVAL,
DL_TIMERG_CAPTURE_COMPARE_0_INDEX);
DL_TimerG_setCaptCompUpdateMethod(PWM_0_INST, DL_TIMER_CC_UPDATE_METHOD_IMMEDIATE, DL_TIMERG_CAPTURE_COMPARE_0_INDEX);
DL_TimerG_enableClock(PWM_0_INST);
DL_TimerG_setCCPDirection(PWM_0_INST , DL_TIMER_CC0_OUTPUT );
}
else if(Freq<32000&&Freq>=500)
{
period1 = (uint32_t)(round_num(32000000.0 / Freq));
Comparvalue1=(uint32_t)round_num(period1*(1-Duty));
DL_TimerG_PWMConfig gPWM_0Config_32000_500= {
.pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN,
.period = period1,
.startTimer = DL_TIMER_START,
};
SYSCFG_DL_PWM_0_init_custom(gPWM_ClockConfig_32000_500,gPWM_0Config_32000_500,Comparvalue1 );
}
else if(Freq<500&&Freq>=100)
{
period1 = (uint32_t)(round_num(6400000.0 / Freq));
Comparvalue1=(uint32_t)round_num(period1*(1-Duty));
DL_TimerG_PWMConfig gPWM_0Config_500_100= {
.pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN,
.period = period1,
.startTimer = DL_TIMER_START,
};
SYSCFG_DL_PWM_0_init_custom(gPWM_ClockConfig_500_100, gPWM_0Config_500_100,Comparvalue1 );
}
else if(Freq<100&&Freq>=20)
{
period1 = (uint32_t)(round_num(1000000.0 / Freq));
Comparvalue1=(uint32_t)round_num(period1*(1-Duty));
DL_TimerG_PWMConfig gPWM_0Config_100_20= {
.pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN,
.period = period1,
.startTimer = DL_TIMER_START,
};
SYSCFG_DL_PWM_0_init_custom(gPWM_ClockConfig_100_20, gPWM_0Config_100_20,Comparvalue1 );
}
else if(Freq<20&&Freq>=4)
{
period1 = (uint32_t)(round_num(200000.0 / Freq));
Comparvalue1=(uint32_t)round_num(period1*(1-Duty));
DL_TimerG_PWMConfig gPWM_0Config_20_4= {
.pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN,
.period = period1,
.startTimer = DL_TIMER_START,
};
SYSCFG_DL_PWM_0_init_custom(gPWM_ClockConfig_20_4,gPWM_0Config_20_4,Comparvalue1 );
}
else if(Freq<4&&Freq>=1)
{
period1 = (uint32_t)(round_num(40000.0 / Freq));
Comparvalue1=(uint32_t)round_num(period1*(1-Duty));
DL_TimerG_PWMConfig gPWM_0Config_4_1= {
.pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN,
.period = period1,
.startTimer = DL_TIMER_START,
};
SYSCFG_DL_PWM_0_init_custom(gPWM_ClockConfig_4_1, gPWM_0Config_4_1,Comparvalue1 );
}
}
else if(pwmChannel==CHANNEL_2)
SYSCONFIG_WEAK void SYSCFG_DL_PWM_0_init_custom(DL_TimerG_ClockConfig gPWM_0ClockConfig,DL_TimerG_PWMConfig gPWM_0Config,uint32_t Comparvalue)
{
DL_TimerG_setClockConfig(PWM_0_INST, (DL_TimerG_ClockConfig *) &gPWM_0ClockConfig);
DL_TimerG_initPWMMode(PWM_0_INST, (DL_TimerG_PWMConfig *) &gPWM_0Config);
DL_TimerG_setCaptureCompareValue(PWM_0_INST, Comparvalue, DL_TIMER_CC_0_INDEX);
DL_TimerG_setCaptureCompareOutCtl(PWM_0_INST, DL_TIMER_CC_OCTL_INIT_VAL_LOW,
DL_TIMER_CC_OCTL_INV_OUT_DISABLED, DL_TIMER_CC_OCTL_SRC_FUNCVAL,
DL_TIMERG_CAPTURE_COMPARE_0_INDEX);
DL_TimerG_setCaptCompUpdateMethod(PWM_0_INST, DL_TIMER_CC_UPDATE_METHOD_IMMEDIATE, DL_TIMERG_CAPTURE_COMPARE_0_INDEX);
DL_TimerG_enableClock(PWM_0_INST);
DL_TimerG_setCCPDirection(PWM_0_INST , DL_TIMER_CC0_OUTPUT );
}
UART打印代码如下:
在中断中将标志位置1后在主函数中执行打印的代码。这里为了更加清晰地描述频率及其占空比,直接打印(1-CompareValue)/PWM period Count,因为直接从示波器上无法精确显示当前的频率和占空比,因此使用这种方式准确显示当前占空比。
if(UARTflag==true)
{
uint32_t Freq1=PWM1_Freq,Freq2=PWM2_Freq;
double Duty1=PWM1_Duty,Duty2=PWM2_Duty;
uint32_t Duty_Value1=period1-Comparvalue1;
uint32_t Duty_Value2=period2-Comparvalue2;
printf("Channel 1 PA26:\r\n");
printf("Frequency:%d Duty:%d/%d\r\n",Freq1,Duty_Value1,period1);
printf("Channel 2 PA22:\r\n");
printf("Frequency:%d Duty:%d/%d\r\n",Freq2,Duty_Value2,period2);
printf("All Channel working\r\n");
UARTflag=false;
}
void TIMER_0_INST_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST))
{
case DL_TIMER_IIDX_ZERO:
UARTflag=true;
break;
default:
break;
}
}
5 遇到的主要难题
九键输入的代码
一开始逻辑非常混乱,表示当前模式的标志也只是使用bool类型,逻辑表述不清楚导致代码总是死机,最后使用枚举类型来表示一个循环的模式切换的概念,从而使模式切换以及九键的 输入逻辑变得清晰。
PWM波设置的代码
PWM波需要分别针对不同的频率配不同的CLockConfig和PWM_Config,第一是需要整理清楚在外在的输出某个PWM波的频率的表象下,内在定时器的配置是什么逻辑,第二则是默认的CompareValue是由低电平翻转到高电平的值,因此和“占空比”的概念是相反的,需要格外注意
6 未来的计划建议
该项目已经成功实现了简易示波器的功能,并达到了预期指标。
- 可以使用更好的OLED屏幕以显示更多信息
- 可以增加PWM通道
- 可以更加细化频率的时钟配置,这样可以提高低频时的占空比调节精度
- 可以不使用九键输入,可以用按键实现加加减减的功能,如此可以降低使用的按键,开发其他功能的按键,例如开关UART等等