一、项目介绍
本项目基于 EVM_MSPM0L1306开发板 实现的一个简易PWM(脉冲宽度调制)发生器。简易PWM(脉冲宽度调制)发生器是一种通过调节脉冲的宽度来控制模拟信号的设备。它利用数字信号对模拟电路进行精确控制,广泛应用于测量、通信和功率控制等领域。可以实现双通道 1Hz ~ 1MHz 的频率调节。
1.1 硬件介绍
EVM_MSPM0L1306开发板专为电赛设计,它搭载了MSPM0L1306微控制器,该芯片基于高性能的Arm® Cortex®-M0+内核,兼具低功耗与高效能特性,为电赛训练提供稳定而强大的处理核心。它搭配 1.3寸 128 * 64 分辨率的OLED显示屏,板载丰富的外设接口,无论是构建复杂的传感器网络、实现精准的数据采集,还是进行高效的通信交互,都能游刃有余。
1.2 功能概览
- 使用MSPM0L1306的定时器,生成2路重复频率 1Hz~1MHz 的 PWM 信号,每一路的重复频率和占空比都可独立调节。
- 使用拔码开关使能2路PWM输出,拔码开关向上拔动时使能当前通道PWM输出,同时点亮通道对应的红色LED灯。 拔码开关向下拔动时,关闭当前通道的PWM输出,同时熄灭当前通道对应的红色LED灯。
- 使用4个按键,每2个按键调整一路PWM的频率和占空比切换。
- PWM频率的调节使用固定频率表进行切换,分别为: 1Hz、10Hz、100Hz、1KHz、10KHz、100KHz、1MHz。
- PWM占空比的调节使用步进形式切换,每次按键增加 10%。0% 占空比可通过使能来实现,因此调节范围为 10% ~ 100%。
- PWM参数(包括两个通道的频率占空比、及通道工作与否)按照1s左右的周期,通过串口发送到上位机。同时核心板上的蓝色LED灯以 0.5Hz的频率闪烁,1秒点亮,1秒熄灭。
- 在OLED屏幕上显示基础信息,当前使用引脚示意、引脚相应的PWM参数。如下图所示:
名称 | 说明 |
---|---|
CHn: Axx | n 表示当前通道号, Axx 表示当前通道输出引脚。 |
F: 1Hz | F 表示频率。 |
D: 20 | D 表示占空比。 |
S: +/o | 当前通道使能状态。 + 表示使能当前通道使能输出, o 表示禁用当前通道输出。 |
1.3 设计思路
单片机定时器生成PWM波形的原理是通过定时器的周期性计数与CCR比较器的值进行比较,从而产生高低电平变化,形成PWM波形。这种方式在电机控制、LED调光等应用中非常有效,能够实现精确的控制。
在MSPM0L1306单片机中,内置了两路CCR比较器,因此可以使用定时器产生两路独立的PWM。定时器可以设置为PWM模式,此模式下定时器根据CCR设置的位置自动生成PWM波形。
基于以上理论,只需要编程实现修改定时器配置以及比较器值即可实现每一路的独立频率与占空比调节。
二、功能实现
2.1 软件流程图
这里借用直播课中的软件流程图来说明一下软件功能,首先,系统进行初始化操作,包括定时器、GPIO、屏幕和UART的初始化。然后,系统进入一个检测按键的状态,根据用户的输入(按下或未按下、拔码开关拔上或拔下),执行不同的操作。如果用户没有按下按钮或拔动拔码开关,系统会发送PWM输出到上位机;如果用户按下了按钮或拔动拔码开关,系统会更改PWM重复频率并更新屏幕内容。
2.2 实现过程
2.2.1 PWM定时器配置
2.2.2 PWM控制
PWM通道使能或禁用使用的拔码开关进行控制,使用2个 GPIO 引脚作为输入。4个按键开关作为2路PWM的频率与占空比切换控制,每路2个按钮分别控制频率和占空比。关键代码如下所示:
// 使能控制
if(DL_GPIO_readPins(PWM_CONTROL_PORT, PWM_CONTROL_CH1_EN_PIN)) {
DL_GPIO_setPins(PWM_STATE_LED_PORT, PWM_STATE_LED_CH_1_PIN);
pwm_ch_0_enable = false;
} else {
DL_GPIO_clearPins(PWM_STATE_LED_PORT, PWM_STATE_LED_CH_1_PIN);
pwm_ch_0_enable = true;
}
if(DL_GPIO_readPins(PWM_CONTROL_PORT, PWM_CONTROL_CH2_EN_PIN)) {
DL_GPIO_setPins(PWM_STATE_LED_PORT, PWM_STATE_LED_CH_2_PIN);
pwm_ch_1_enable = false;
} else {
DL_GPIO_clearPins(PWM_STATE_LED_PORT, PWM_STATE_LED_CH_2_PIN);
pwm_ch_1_enable = true;
}
// 频率与占空比切换
if(DL_GPIO_readPins(PWM_CONTROL_PORT, PWM_CONTROL_CH1_FREQ_PIN)) { // 通道1频率按键按下
pwm_0_freq_index = (pwm_0_freq_index + 1) % PERIOD_TABLE_NUM; // 循环切换通道1频率
need_reload_pwm_0_config = true;
}
if(DL_GPIO_readPins(PWM_CONTROL_PORT, PWM_CONTROL_CH1_DUTY_PIN)) { // 通道1占空比按键按下
pwm_ch_0_duty = (pwm_ch_0_duty + 10); // 循环切换通道1占空比,每次增加 10%,0%占空比可以通过使能来实现,因此这里占空比调节范围为 10% ~ 100%
if(MAX_DUTY < pwm_ch_0_duty) {
pwm_ch_0_duty %= MAX_DUTY;
}
need_reload_pwm_0_config = true;
}
if(DL_GPIO_readPins(PWM_CONTROL_PORT, PWM_CONTROL_CH2_FREQ_PIN)) { // 通道2频率按键按下
pwm_1_freq_index = (pwm_1_freq_index + 1) % PERIOD_TABLE_NUM; // 循环切换通道2频率
need_reload_pwm_1_config = true;
}
if(DL_GPIO_readPins(PWM_CONTROL_PORT, PWM_CONTROL_CH2_DUTY_PIN)) { // 通道2占空比按键按下
pwm_ch_1_duty = (pwm_ch_1_duty + 10); // 循环切换通道2占空比,每次增加 10%,0%占空比可以通过使能来实现,因此这里占空比调节范围为 10% ~ 100%
if(MAX_DUTY < pwm_ch_1_duty) {
pwm_ch_1_duty %= MAX_DUTY;
}
need_reload_pwm_1_config = true;
}
2.2.3 OLED输出
使用硬件SPI驱动OLED显示,sysconfig 的OLED SPI配置如下图所示:
驱动代码直接使用的示例工程 OLED_SPI 中的驱动代码,OLED显示布局关键代码如下:
OLED_Init();
OLED_Clear();
OLED_ShowString(2, 0, "CH0:A16");
OLED_ShowString(70, 0, "CH1:A15");
OLED_ShowString(2, 2, "F: ");
OLED_ShowString(26, 2, (char *)period_names[pwm_0_freq_index]);
OLED_ShowString(70, 2, "F: ");
OLED_ShowString(94, 2, (char *)period_names[pwm_1_freq_index]);
OLED_ShowString(2, 4, "D: ");
OLED_ShowNum(26, 4, pwm_ch_0_duty, 3, 14);
OLED_ShowString(70, 4, "D: ");
OLED_ShowNum(94, 4, pwm_ch_1_duty, 3, 14);
OLED_ShowString(2, 6, "S: ");
OLED_ShowChar(26, 6, pwm_ch_0_enable ? '+' : 'O');
OLED_ShowString(70, 6, "S: ");
OLED_ShowChar(94, 6, pwm_ch_1_enable ? '+' : 'O');
2.2.4 串口上报PWM状态
上图为 sysconfig 的串口配置截图,串口输出代码使用了老师提供的 printf
重定向到串口打印函数,关键代码如下:
printf("CH0 frea: %s, duty: %3d%%, state: %3s\r\n", period_names[pwm_0_freq_index], pwm_ch_0_duty, pwm_ch_0_enable ? "ON" : "OFF");
printf("CH1 frea: %s, duty: %3d%%, state: %3s\r\n", period_names[pwm_1_freq_index], pwm_ch_1_duty, pwm_ch_1_enable ? "ON" : "OFF");
上位机串口接收效果如下图所示:
三、功能展示
上图为通道1使能,频率为 1KHz,20%占空比,使用便捷示波器可以看到占空比精度20%是能达到要求的。但该示波器显示的频率抖动有点大,好像跟采样频率有关。我使用朋友的示波器显示是正确的。
上图为2通道使能,为了便于观察2路通道使用了相同的频率,不同的占空比来使用示波器方便查看。实测我示波器的通道2频率统计误差比较大,图中显示到了13K,实际是10K。使用示波器通道1查看时频率正常。
由于我手上的示波器最大只能统计到几 10KHz 左右的频率,再大频率统计就不正常了,因此就没有进行更大频率的测试。
四、总结
遇到的问题
- 串口打印过长的字符串时会导致程序跑飞,这个问题折磨了我好久,某一天翻看群里聊天记录时突然灵感来临,尝试修改启动文件 startup_mspm0l1306_uvision.s 中的 Stack_Size EQU 0x00000200 后问题解决。
- OLED显示屏刷新过快导致屏幕乱码,原本打算在主循环中刷新OLED显示,结果一两次刷新过后导致程序跑飞。
- 按键矩阵扫描,原本打算使用按键矩阵中的8个按键控制两路PWM的频率、占空比以及使能。奈何一直无法静下心来思考如何优雅地实现矩阵按键扫描以及消抖,最终使用 4 个按键 + 两个拔码开关来实现控制。
心得体会
本次活动我第一次使用 TI 设计生产的单片机,第一次使用 Keil MDK 开发项目。第一次接触到 sysconfig 这个外设配置工具。刚开始拿到开发板时还茫然不知所措,好在开发板设计方提供的开发环境搭建资料比较详细。按照教程很快安装好了开发调试环境,经过漫长的学习摸索过程实现本次任务目标。
最后感谢电子森林推出的这次活动,对于我来说是个很好的学习机会,理论结合实践。我们下期活动再见!