项目背景
功能介绍
任务描述
- 使用按键或拨码开关组合调节输出频率、占空比,并由按键控制每一路PWM信号的输出。
- 能在OLED屏幕上显示基础信息,当前使用引脚示意、引脚相应的PWM参数。
- 将PWM参数(包括两个通道的频率占空比、及通道工作与否)按照1s左右的周期,通过串口发送到上位机
硬件介绍
这款微控制器采用TI MSPM0L1306,性能卓越,适用于各种嵌入式应用。
核心板通过Type-C接口供电,转换为3.3V电压供给MCU,同时配备多种外设和接口,包括ADC、DAC、UART、SPI、I2C、PWM等功能。
底板也采用Type-C接口供电,为核心板提供电源,并输出5V、3.3V电压供外部使用。底板集成了Debug调试器和虚拟串口,支持SWD模式进行代码烧录和调试,以及通过虚拟串口进行通信。此外,底板还集成了SPI协议的1.3寸OLED显示屏、FLASH存储器、陀螺仪、IIC协议的EEPROM,以及常见的矩阵键盘、蜂鸣器、8位LED、8位拨码开关等外设。用户还可根据需求选择集成蓝牙或WiFi无线模块。
硬件连接:
PWM通道0使用定时器TIMG0,PA16作为输出引脚。PWM通道1使用定时器TIMG1,PA26作为输出引脚。
将这两个引脚与示波器或逻辑分析仪的端口连接,观察PWM波形频率和占空比
设计思路
主要困难
此小节主要分析功能需求,实现该项目会面临的困难。
从任务描述中可以看出,实现该任务有三个核心功能:
- 独立调节两路信号的重复频率和占空比。使能两个独立的定时器,设置定时器的分频系数和计数值。
- 显示PWM基础信息,例如引脚示意、引脚相应的PWM参数。
- 驱动按键
解决思路
核心功能一
为了使每一路的重复频率和占空比都可独立调节,两路PWM信号的输出应由不同的定时器产生
表格的1/10000是芯片定时器外设至少能达到的精度。
由于定时器装载值位数的限制(0~65535),当PWM时钟频率为32MHz时,必须要对时钟频率进行分频和Prescale,才能达到100Hz和1Hz的重复频率。
下面将对不同PWM频率进行参数配置,说明所选方案能达到规定的占空比精度和占空比调节精度。
调节占空比只需修改PWM Period Count即可
频率为1MHz,100KHz,10KHz,1KHz,分频配置如下
Divider=1,Prescale=1
PWM Period Count =32,PWM频率为1MHz,当设定的占空比为50%时,实际装载值为16,实际占空比约为48%,精度能达到2%,占空比精度符合题目要求的1/10。
占空比调节精度 1/32 符合1/10精度要求
以此类推,100KHz
50%占空比的波形,实际占空比约为49.84%,差值为0.16%,0.0016<0.01,占空比精度符合1/100的占空比的精度要求
占空比调节精度 1/320 符合1/100 精度要求
10KHz
50%占空比的波形,实际占空比约为49.984%,差值为0.016%,0.00016<0.001,占空比精度符合1/1000的占空比的精度要求
占空比调节精度 1/3200 符合1/1000 精度要求
1KHz,PWM Period Count为32000
50%占空比的波形,实际占空比约为49.9984%,差值为0.0016%,0.000016<0.0001,占空比精度符合1/1000的精度要求
占空比调节精度 1/32000 符合1/10000 精度要求
100Hz,分频配置如下为Divider=1,Prescale=10,PWM Period Count为32000
50%占空比的波形,实际占空比约为49.9984%,差值为0.0016%,0.000016<0.0001,占空比精度符合1/1,000的占空比的精度要求
占空比调节精度 1/32000 符合题目要求的1/10000
1Hz,分频配置为Divider=1,Prescale=100,PWM Period Count为32000
50%占空比的波形,实际占空比约为49.9984%,差值为0.0016%,0.000016<0.0001,占空比精度符合1/1,000的占空比的精度要求
占空比调节精度 1/32000 符合1/10000精度要求
可以看到,占空比调节精度与较大定时器计数值有关,较大的定时器计数值可实现较小的偏差。
在此说明的是,以上只是在sysconfig中查看配置参数的效果,实际这些Divider、Prescale、PWM Period Count等参数的设置的步骤将在代码中实现。
综上所述,占空比精度和占空比调节精度的要求和最终实现如下表所示。
重复频率 | 占空比调节精度 | 达到的占空比调节精度 | 占空比精度 | 达到的占空比精度 |
|---|---|---|---|---|
1MHz | 1/10 | 1/32 | 1/10 | 0.02 |
100KHz | 1/100 | 1/320 | 1/100 | 0.0016 |
10KHz | 1/1,000 | 1/3200 | 1/1,000 | 0.00016 |
1KHz | 1/10,000 | 1/32000 | 1/1,000 | 0.000016 |
100Hz | 1/10,000 | 1/32000 | 1/1,000 | 0.000016 |
1Hz | 1/10,000 | 1/32000 | 1/1,000 | 0.000016 |
核心功能二
驱动OLED屏幕,通过串口发送到上位机。
借助SPI外设驱动OLED屏幕。
SPI配置如下:
SPI接线方式为
CS→SPI0_CS0→PA2
DC→SPI0_SCK→PA6
D1→SPI0_PICO→PA5
D0→SPI0_POCI→PA4
串口配置为
核心功能三
按键功能如下图所示,共使用10个按键调节PWM占空比和频率。占空比从10%
软件流程图
此小节主要描述解决方案的软件实现,提出实现项目功能的技术路线。
流程图如下图所示。
功能展示
核心代码片段及说明
点击按键切换PWM频率或占空比
PWM通道1的代码与PWM通道0的代码类似
switch (key_num)
{
case 0x01:
// 开启/关闭PWM0
ch0 = !ch0; // 逻辑取反
if (ch0)
{
// 启动PWM通道0的定时器
DL_TimerG_startCounter(PWM_0_INST);
OLED_ShowString(1, 0, " ");
OLED_ShowString(1, 0, "CH0 ON");
set_frequecy(freq_index_ch0, PWM_0_INST);
trans(frequencies[freq_index_ch0]);
OLED_ShowString(1, 1, " ");
OLED_ShowString(1, 1, uart1_data);
set_duty(duty_ch0, 0);
sprintf(uart1_data, "duty %d%%", duty_ch0 * 10);
OLED_ShowString(1, 2, " ");
OLED_ShowString(1, 2, uart1_data);
}
else
{
// 关闭PWM通道0的定时器
DL_TimerG_stopCounter(PWM_0_INST);
// OLED显示数据
OLED_ShowString(1, 0, " ");
OLED_ShowString(1, 0, "CH0 OFF");
OLED_ShowString(1, 1, " ");
OLED_ShowString(1, 2, " ");
}
key_num = 0;
break;
case 0x02:
ch1 = !ch1; // 逻辑取反
// 开启/关闭PWM1
// ...
break;
case 0x05:
// PWM0 频率减小
freq_index_ch0 = dec_freq(freq_index_ch0);
set_frequecy(freq_index_ch0, PWM_0_INST);
duty_ch0 = 5;
set_duty(duty_ch0, 1);
trans(frequencies[freq_index_ch0]);
OLED_ShowString(1, 1, " ");
OLED_ShowString(1, 2, " ");
if (ch0)
{
OLED_ShowString(1, 1, uart1_data);
OLED_ShowString(1, 2, "duty 50%");
}
key_num = 0;
break;
case 0x06:
// PWM0 频率增大
freq_index_ch0 = inc_freq(freq_index_ch0);
set_frequecy(freq_index_ch0, PWM_0_INST);
duty_ch0 = 5;
set_duty(duty_ch0, 0);
// ...
key_num = 0;
break;
case 0x07:
// PWM1 频率减小
// ...
break;
case 0x08:
// PWM1 频率增大
// ...
break;
case 0x09:
key_num = 0;
// PWM0 占空比减小
if (duty_ch0 == 0)
{
duty_ch0 = 10;
}
else
{
duty_ch0 -= 1;
}
set_duty(duty_ch0, 0);
OLED_ShowString(1, 2, " ");
if (ch0)
{
OLED_ShowString(1, 2, uart1_data);
}
break;
case 0x0A:
key_num = 0;
// PWM0 占空比增大
if (duty_ch0 == 10)
{
duty_ch0 = 0;
}
else
{
duty_ch0 += 1;
}
set_duty(duty_ch0, 0);
// ...
break;
case 11:
key_num = 0;
// PWM1 占空比减小
// ...
break;
case 12:
key_num = 0;
// PWM1 占空比增大
// ...
break;
default:
break;
}
设置频率
void set_frequecy(uint32_t freq_index, GPTIMER_Regs *gptimer)
{
uint32_t period = 0;
switch (freq_index)
{
case 0: // 1Hz
gptimer->CLKDIV = (uint32_t)(DL_TIMER_CLOCK_DIVIDE_5);
gptimer->COMMONREGS.CPS = (199U);
period = 32000;
break;
case 1: // 100Hz
gptimer->CLKDIV = (uint32_t)(DL_TIMER_CLOCK_DIVIDE_1);
gptimer->COMMONREGS.CPS = (9U);
period = 32000;
break;
case 2: // 1KHz
gptimer->CLKDIV = (uint32_t)(DL_TIMER_CLOCK_DIVIDE_1);
gptimer->COMMONREGS.CPS = (0U);
period = 32000;
break;
case 3: // 10KHz
gptimer->CLKDIV = (uint32_t)(DL_TIMER_CLOCK_DIVIDE_1);
gptimer->COMMONREGS.CPS = (0U);
period = 3200;
break;
case 4: // 100KHz
gptimer->CLKDIV = (uint32_t)(DL_TIMER_CLOCK_DIVIDE_1);
gptimer->COMMONREGS.CPS = (0U);
period = 320;
break;
case 5: // 1MHz
gptimer->CLKDIV = (uint32_t)(DL_TIMER_CLOCK_DIVIDE_1);
gptimer->COMMONREGS.CPS = (0U);
period = 32;
break;
default:
break;
}
DL_Timer_setLoadValue(gptimer, (period - (uint32_t)1));
DL_Timer_setCaptureCompareAction(gptimer,
(DL_TIMER_CC_LACT_CCP_HIGH | DL_TIMER_CC_CDACT_CCP_LOW),
DL_TIMER_CC_0_INDEX);
}
设置占空比
const int frequencies[NUM_FREQUENCIES] = {1, 100, 1000, 10000, 100000, 1000000};
const int periods[NUM_FREQUENCIES] = {32000, 32000, 32000, 3200, 320, 32};
void set_duty(uint8_t duty, uint8_t channel) // 占空比是duty的10倍
{
uint32_t compareValue;
if (channel == 0)
{
compareValue = (uint32_t)(periods[freq_index_ch0] * (10 - duty) / 10);
DL_TimerG_setCaptureCompareValue(PWM_0_INST, compareValue, DL_TIMER_CC_0_INDEX);
compareValue_ch0 = compareValue;
}
else if (channel == 1)
{
compareValue = (uint32_t)(periods[freq_index_ch1] * (10 - duty) / 10);
DL_TimerG_setCaptureCompareValue(PWM_1_INST, compareValue, DL_TIMER_CC_0_INDEX);
compareValue_ch1 = compareValue;
}
}
测试结果
上位机接收PWM参数
OLED显示PWM参数
设置占空比为50%的情况下,各频率实际占空比结果如下:
1Hz
100Hz
1KHz
10KHz
100KHz
1MHz
可以看到,占空比精度基本符合要求。
具体频率和占空比的调节效果可观看演示视频。
总结
本项目依托EVM_MSPM0L1306开发套件,完成简易PWM发生器功能。在此过程中编写外设驱动代码,学习定时器输出PWM原理,配置定时器参数以独立调节两路PWM重复频率和占空比,最终顺利完成任务。