基于MSPM0L1306的简易PWM发生器
该项目使用了EVM_MSPM0L1306开发套件,实现了PWM发生器的设计,它的主要功能为:可调节PWM信号的输出频率、占空比,并将PWM参数显示于OLED上,发送到上位机。
标签
PWM
定时器
EVM_MSPM0L1306
TetraPak
更新2024-07-18
301

项目背景

功能介绍

任务描述

  • 使用按键或拨码开关组合调节输出频率、占空比,并由按键控制每一路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无线模块。

核心板引脚分配.png

硬件连接:

PWM通道0使用定时器TIMG0,PA16作为输出引脚。PWM通道1使用定时器TIMG1,PA26作为输出引脚。

将这两个引脚与示波器或逻辑分析仪的端口连接,观察PWM波形频率和占空比

设计思路

主要困难

此小节主要分析功能需求,实现该项目会面临的困难。

从任务描述中可以看出,实现该任务有三个核心功能:

  1. 独立调节两路信号的重复频率和占空比。使能两个独立的定时器,设置定时器的分频系数和计数值。
  2. 显示PWM基础信息,例如引脚示意、引脚相应的PWM参数。
  3. 驱动按键

解决思路

核心功能一

为了使每一路的重复频率和占空比都可独立调节,两路PWM信号的输出应由不同的定时器产生
表格的1/10000是芯片定时器外设至少能达到的精度。

由于定时器装载值位数的限制(0~65535),当PWM时钟频率为32MHz时,必须要对时钟频率进行分频和Prescale,才能达到100Hz和1Hz的重复频率。

下面将对不同PWM频率进行参数配置,说明所选方案能达到规定的占空比精度占空比调节精度

调节占空比只需修改PWM Period Count即可

频率为1MHz,100KHz,10KHz,1KHz,分频配置如下

Divider=1,Prescale=1

image.png

PWM Period Count =32,PWM频率为1MHz,当设定的占空比为50%时,实际装载值为16,实际占空比约为48%,精度能达到2%,占空比精度符合题目要求的1/10。

占空比调节精度 1/32 符合1/10精度要求

image.png



以此类推,100KHz

50%占空比的波形,实际占空比约为49.84%,差值为0.16%,0.0016<0.01,占空比精度符合1/100的占空比的精度要求

占空比调节精度 1/320 符合1/100 精度要求

image.png

10KHz

50%占空比的波形,实际占空比约为49.984%,差值为0.016%,0.00016<0.001,占空比精度符合1/1000的占空比的精度要求

占空比调节精度 1/3200 符合1/1000 精度要求

image.png

1KHzPWM Period Count为32000

50%占空比的波形,实际占空比约为49.9984%,差值为0.0016%,0.000016<0.0001,占空比精度符合1/1000的精度要求

占空比调节精度 1/32000 符合1/10000 精度要求

image.png

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

image.png


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精度要求

image.png

可以看到,占空比调节精度与较大定时器计数值有关,较大的定时器计数值可实现较小的偏差。

在此说明的是,以上只是在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配置如下:
image.png

SPI接线方式为

CS→SPI0_CS0→PA2

DC→SPI0_SCK→PA6

D1→SPI0_PICO→PA5

D0→SPI0_POCI→PA4


串口配置为

image.png

核心功能三

按键功能如下图所示,共使用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参数

image.png

OLED显示PWM参数

设置占空比为50%的情况下,各频率实际占空比结果如下:

1Hz

100Hz

1KHz

10KHz

100KHz

1MHz

可以看到,占空比精度基本符合要求。

具体频率和占空比的调节效果可观看演示视频

总结

本项目依托EVM_MSPM0L1306开发套件,完成简易PWM发生器功能。在此过程中编写外设驱动代码,学习定时器输出PWM原理,配置定时器参数以独立调节两路PWM重复频率和占空比,最终顺利完成任务。

附件下载
PWMGenerator.zip
工程文件
团队介绍
电子爱好者
团队成员
TetraPak
评论
0 / 100
查看更多
猜你喜欢
基于MSPM0L1306实现简易PWM波发生器该项目使用了MSPM0L1306,实现了生成2路重复频率1Hz~1MHz的PWM信号,每一路的重复频率和占空比都可独立调节的设计,它的主要功能为:其中,PWM重复频率在1Hz~10KHz内连续可调,通过按键切换至100KHz和1MHz。 PWM重复频率越高,占空比分辨率降低(主时钟32MHz为例)。
sty
232
基于MSPM0L1306的简易PWM发生器该项目使用了EVM_MSPM0L1306,实现了简易PWM的设计,它的主要功能为:使用MSPM0L1306的定时器,生成2路重复频率1Hz~1MHz的PWM信号,每一路的重复频率和占空比都可独立调节。 其中,PWM重复频率在1Hz~10KHz内连续可调,通过按键切换至100KHz和1MHz。 PWM重复频率越高,占空比分辨率降低(主时钟32MHz为例) 重复频率 占空比调节精度 1MHz 1/10 100KHz 1/100 10KHz 1/1,000 1KHz 1/10,000 100Hz 1/10,000 1Hz 1/10,000 PWM重复频率在1Hz~10KHz时,占空比的精度不低于1/1000,100Khz和1Mhz的占空比精度按照表格即可。 使用按键或拨码开关组合调节输出频率、占空比,并由按键控制每一路PWM信号的输出。 能在OLED屏幕上显示基础信息,当前使用引脚示意、引脚相应的PWM参数。 将PWM参数(包括两个通道的频率占空比、及通道工作与否)按照1s左右的周期,通过串口发送到上位机。。
冲向天空的猪
218
基于MSPM0L1306实现简易PWM发生器该项目使用了MSPM0L1306,实现了简易PWM发生器的设计,它的主要功能为:生成两路独立且可调节的PWM信号,每路信号的重复频率范围从1Hz到1MHz,其中在1Hz到10kHz范围内,用户可以实现连续的频率调节;在1Hz至10kHz的频率范围内,PWM信号的占空比精度不低于1/10000。而在100kHz和1MHz的高频模式下,占空比精度会自动调整;OLED屏幕实时显示当前PWM信号的状态,包括所使用的通道、频率和占空比等参数。每隔大约1秒,该系统会通过串口通信将所有PWM参数(包括两路信号的频率、占空比以及是否正在工作)发送至上位机,以便于监控和记录。。
Samaritan
102
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号