基于EVM_MSPM0L1306开发平台实现的简易双通道PWM发生器
该项目使用了EVM_MSPM0L1306开发平台,实现了简易双通道PWM发生器的设计,它的主要功能为:生成2路重复频率1Hz~1MHz的PWM信号,每一路的重复频率和占空比都可独立调节。 其中,PWM重复频率在1Hz~11MHz内连续可调,占空比和频率调节精度极高,基本可以达到sysconfig的精细度,具有人性化的用户界面,多级菜单,矩阵键盘输入等等多样化的拓展内容。
标签
嵌入式系统
显示
开发板
武汉启是科技
硬禾科技
MSPM0培训活动
彭晨峰
更新2024-07-22
17

1 项目需求

  • 使用MSPM0L1306的定时器,生成2路重复频率1Hz~1MHz的PWM信号,每一路的重复频率和占空比都可独立调节。

重复频率

占空比调节精度

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左右的周期,通过串口发送到上位机。
  • 其中,PWM重复频率在1Hz~10KHz内连续可调,通过按键切换至100KHz和1MHz。
  • PWM重复频率越高,占空比分辨率降低(主时钟32MHz为例)

2 完成的功能及达到的性能

2.1 输出两路PWM波

配置sysconfig的定时器PWM输出(目前配置了通道一:PA21,通道二:PA26),使用两个不同的定时器,从而实现两个不同通道的重复频率和占空比的独立调节

2.2 调节重复频率

实现1Hz~1MHz的PWM重复频率连续可调,频率计算与sysconfig一致(ARR=SYSCLK/freq+1

(注意:调整重复频率通过调整预装载寄存器的值实现,会导致占空比发生变化,通过提前取出占空比并重新计算赋值,尽可能保证占空比不变,如需更加精确,请重新调节占空比)

2.3 调节占空比

实现精度不低于0.0001%的占空比连续调节,通过计算,完全符合规则要求的“对应持续时间的精度”达到表格中的要求

2.4 PWM波输出与否

通过操作PWM的时钟开关来实现对PWM波输出与否的控制

2.5 矩阵键盘与多级菜单

采用扫描的方式实现4*4矩阵键盘,通过矩阵键盘与OLED显示屏实现多级菜单,可操作调节输出频率、占空比,以及控制每一路PWM信号的输出。

2.6 占空比精度

占空比精度通过浮点运算以及标准计算公式尽可能逼近实际。

2.7 定时器中断和串口输出

3 实现思路

  • 两个独立的定时器分别生成一路PWM信号
  • 将信号相关参数在OLED上边显示
  • 按下按键操作菜单独立调整两路信号的重复频率、占空比和输出与否。
  • 将信号相关变化参数在OLED上边显示

4 实现过程

4.1 程序流程图 

image.png

4.2 OLED函数

为了大家查看方便,我就不把例程中已经有的放出来了,说一说自己写的

为了方便编辑,我使用了新的字库数组,应用到新的显示中文的函数当中,当然逻辑上是一样的,但是这个函数说实话不是很好用,每次都只能显示一个字,而且还不支持字符串直接输出,要写上数组下标才行。

//显示汉字

void OLED_ShowMyCHinese(u8 x,u8 y,u8 no)

{

u8 t,adder=0;

OLED_Set_Pos(x,y);

for(t=0;t<16;t++)

{

OLED_WR_Byte(MYlib[2*no][t],OLED_DATA);

adder+=1;

}

OLED_Set_Pos(x,y+1);

for(t=0;t<16;t++)

{

OLED_WR_Byte(MYlib[2*no+1][t],OLED_DATA);

adder+=1;

}

}

除此之外,为了更方便的显示浮点数,我编写了一个显示小数点函数

//显示小数
void OLED_ShowFloatNum(uint8_t X, uint8_t Y, double Number, uint8_t IntLength, uint8_t FraLength, uint8_t FontSize)
{
uint32_t PowNum, IntNum, FraNum;

/*提取整数部分和小数部分*/
IntNum = Number; //直接赋值给整型变量,提取整数
Number -= IntNum; //将Number的整数减掉,防止之后将小数乘到整数时因数过大造成错误
PowNum = oled_pow(10, FraLength); //根据指定小数的位数,确定乘数
FraNum = round(Number * PowNum); //将小数乘到整数,同时四舍五入,避免显示误差
IntNum += FraNum / PowNum; //若四舍五入造成了进位,则需要再加给整数

/*显示整数部分*/
OLED_ShowNum(X, Y, IntNum, IntLength, FontSize);

/*显示小数点*/
OLED_ShowChar(X + (IntLength) * FontSize/2, Y, '.');

/*显示小数部分*/
OLED_ShowNum(X + (IntLength + 1) * FontSize/2, Y, FraNum, FraLength, FontSize);
}

最后附上新增字库

unsigned char  MYlib[][32]={

{0x40,0x42,0x44,0xcc,0x00,0x00,0xf1,0x91,0x95,0xf9,0x95,0x93,0xf9,0x10,0x00,0x00},
{0x00,0x40,0x20,0x1f,0x20,0x40,0xbf,0x84,0x84,0xbf,0x94,0xa4,0x9f,0xc0,0x40,0x00},//通0

{0x40,0x42,0x44,0xcc,0x00,0x04,0xe5,0xb6,0xac,0xa4,0xa4,0xa6,0xf5,0x24,0x00,0x00},
{0x00,0x40,0x20,0x1f,0x20,0x40,0xbf,0xa4,0xa4,0xa4,0xa4,0xa4,0xbf,0xc0,0x40,0x00},//道1

{0x40,0x7c,0x40,0xff,0x48,0x6c,0x4a,0xf2,0x12,0x1a,0xd6,0x12,0x12,0xfb,0x12,0x00},
{0x08,0x87,0x40,0x2f,0x10,0x0f,0x80,0x8f,0x40,0x20,0x1f,0x20,0x40,0xcf,0x00,0x00},//频2

{0x00,0x14,0xa4,0x44,0x04,0x24,0xb5,0x6e,0x24,0x94,0x04,0x44,0xa6,0x14,0x00,0x00},
{0x08,0x09,0x08,0x08,0x09,0x09,0x09,0xff,0x09,0x09,0x0b,0x08,0x08,0x0d,0x08,0x00},//率3

{0x00,0x00,0x80,0x80,0x80,0x80,0xff,0x88,0x88,0x88,0x88,0xc8,0x8c,0x08,0x00,0x00},
{0x00,0x00,0xff,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xff,0x00,0x00,0x00,0x00},//占4

{0x10,0x0c,0x84,0x44,0x24,0x34,0x05,0x06,0x04,0x14,0x24,0x64,0xc4,0x14,0x0c,0x00},
{0x40,0x40,0x42,0x42,0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x42,0x42,0x60,0x40,0x00},//空5

{0x00,0x00,0xfe,0x40,0x40,0x60,0x40,0x00,0xff,0x80,0x40,0x20,0x30,0x00,0x00,0x00},
{0x00,0x40,0x7f,0x20,0x20,0x10,0x10,0x00,0x3f,0x40,0x40,0x40,0x40,0x40,0x78,0x00},//比6

{0x00,0x08,0x30,0x00,0xff,0x20,0x20,0x20,0x20,0xff,0x20,0x22,0x24,0x30,0x20,0x00},
{0x08,0x0c,0x02,0x01,0xff,0x40,0x20,0x1c,0x03,0x00,0x03,0x0c,0x30,0x60,0x20,0x00},//状7

{0x04,0x04,0x84,0x84,0x44,0x24,0x54,0x8f,0x14,0x24,0x44,0x44,0x84,0x86,0x84,0x00},
{0x01,0x21,0x1c,0x00,0x3c,0x40,0x42,0x4c,0x40,0x40,0x70,0x04,0x08,0x31,0x00,0x00},//态8

{0x80,0x82,0x82,0x82,0xfe,0x82,0x82,0x82,0x82,0x82,0xfe,0x82,0x83,0xc2,0x80,0x00},
{0x00,0x80,0x40,0x30,0x0f,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00},//开9

{0x00,0x00,0x00,0xfc,0x44,0x44,0x44,0x45,0x46,0x44,0x44,0x44,0x44,0x7e,0x04,0x00},
{0x40,0x20,0x18,0x07,0xfe,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0xff,0x02,0x00},//启10

{0x00,0x10,0x10,0x11,0x12,0x1c,0x10,0xf0,0x10,0x18,0x14,0x13,0x1a,0x90,0x00,0x00},
{0x81,0x81,0x41,0x41,0x21,0x11,0x0d,0x03,0x0d,0x11,0x21,0x21,0x41,0xc1,0x41,0x00},//关11

{0x00,0xf8,0x01,0x22,0x26,0x20,0x22,0xa2,0xfa,0x22,0x32,0x22,0x02,0xff,0x02,0x00},
{0x00,0xff,0x00,0x08,0x04,0x02,0x21,0x40,0x3f,0x00,0x00,0x40,0x80,0x7f,0x00,0x00},//闭12

{0x04,0x34,0xc4,0x04,0xc4,0x3c,0x20,0x10,0x0f,0xe8,0x08,0x08,0x28,0x18,0x00,0x00},
{0x10,0x08,0x06,0x01,0x82,0x8c,0x40,0x30,0x0c,0x03,0x0c,0x10,0x60,0xc0,0x40,0x00},//欢13

{0x40,0x42,0x44,0xc8,0x00,0xfc,0x04,0x02,0x82,0xfc,0x04,0x04,0x04,0xfe,0x04,0x00},
{0x00,0x40,0x20,0x1f,0x20,0x47,0x42,0x41,0x40,0x7f,0x40,0x42,0x44,0x63,0x20,0x00},//迎14

{0x40,0x20,0xf8,0x07,0x04,0xf4,0x14,0x14,0x14,0xff,0x14,0x14,0x14,0xf6,0x04,0x00},
{0x00,0x00,0xff,0x00,0x80,0x43,0x45,0x29,0x19,0x17,0x21,0x21,0x41,0xc3,0x40,0x00},//使15

{0x00,0x00,0xfe,0x22,0x22,0x22,0x22,0xfe,0x22,0x22,0x22,0x22,0xff,0x02,0x00,0x00},
{0x80,0x60,0x1f,0x02,0x02,0x02,0x02,0x7f,0x02,0x02,0x42,0x82,0x7f,0x00,0x00,0x00},//用16

{0x04,0x34,0xc4,0x04,0xc4,0x3c,0x00,0x04,0x7c,0x84,0x04,0x04,0xc4,0x3e,0x04,0x00},
{0x40,0x30,0x0c,0x03,0x0c,0xb0,0x80,0x40,0x20,0x13,0x0c,0x1a,0x61,0xc0,0x40,0x00},//双17

{0x00,0x10,0x18,0x16,0x10,0x90,0xf0,0x9f,0x90,0x90,0x92,0x94,0x10,0x18,0x10,0x00},
{0x40,0x20,0x90,0x88,0x46,0x41,0x23,0x14,0x08,0x14,0x22,0x21,0x40,0xc0,0x40,0x00},//发18

{0x00,0xc0,0x30,0x1e,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0x10,0x18,0x10,0x00,0x00},
{0x41,0x40,0x42,0x42,0x42,0x42,0x42,0x7f,0x42,0x42,0x42,0x43,0x42,0x60,0x40,0x00},//生19

{0x80,0x80,0x9f,0x91,0x91,0x91,0x9f,0xe0,0x9f,0x91,0xb1,0xd1,0x9f,0xc0,0x80,0x00},
{0x08,0x08,0xfc,0x8c,0x8a,0x8a,0xf9,0x00,0xf9,0x8a,0x8a,0x8c,0xfc,0x08,0x08,0x00},//器20

{0x84,0x84,0x44,0xe4,0x54,0x4c,0x44,0x7f,0x44,0x4c,0x54,0xe4,0x44,0xc6,0x44,0x00},
{0x40,0x40,0x40,0x5f,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x5f,0x40,0x60,0x40,0x00},//查21

{0x20,0x2a,0x2a,0xaa,0xea,0xba,0xae,0xaa,0xaa,0xa9,0xa9,0xed,0xa8,0x20,0x20,0x00},
{0x04,0x02,0x01,0xff,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0xff,0x00,0x00,0x00,0x00},//看22

{0x20,0x22,0xec,0x00,0x20,0x22,0xaa,0xaa,0xaa,0xbf,0xaa,0xaa,0xeb,0xa2,0x20,0x00},
{0x00,0x00,0x7f,0x20,0x10,0x00,0xff,0x0a,0x0a,0x0a,0x4a,0x8a,0x7f,0x00,0x00,0x00},//请23

{0x10,0x10,0x90,0xff,0x90,0xa0,0x98,0x88,0x88,0xe9,0x8a,0x88,0x88,0xa8,0x98,0x00},
{0x01,0x41,0x80,0x7f,0x00,0x00,0x80,0x84,0x4b,0x30,0x10,0x28,0x47,0xc0,0x00,0x00},//按24

{0x80,0x40,0x20,0xf8,0x07,0x24,0x24,0x24,0x25,0x26,0x24,0x24,0xb4,0x26,0x04,0x00},
{0x00,0x00,0x00,0xff,0x00,0x01,0xfd,0x45,0x45,0x45,0x45,0x45,0xfd,0x01,0x00,0x00},//信25

{0x80,0x80,0x80,0xbe,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xbf,0x82,0xc0,0x80,0x00},
{0x00,0x00,0x00,0x04,0x06,0x05,0x04,0x04,0x44,0x84,0x44,0x3e,0x04,0x00,0x00,0x00},//号26

};


4.3 菜单总览

总共四个Page,实现菜单显示

void page0(void)//开机画面
{
OLED_ShowMyCHinese(36, 1, 13);OLED_ShowMyCHinese(50, 1, 14);OLED_ShowMyCHinese(66, 1, 15);OLED_ShowMyCHinese(82, 1, 16);

OLED_ShowMyCHinese(42, 3, 17);OLED_ShowMyCHinese(58, 3, 0);OLED_ShowMyCHinese(74, 3, 1);

OLED_ShowString(10, 5, "PWM");OLED_ShowMyCHinese(34, 5, 25);OLED_ShowMyCHinese(50, 5, 26);OLED_ShowMyCHinese(66, 5, 18);OLED_ShowMyCHinese(82, 5, 19);OLED_ShowMyCHinese(98, 5, 20);

}

void page1(void)//主页
{
OLED_ShowMyCHinese(2, 0, 21);OLED_ShowMyCHinese(18, 0, 22);OLED_ShowMyCHinese(34, 0, 0);OLED_ShowMyCHinese(50, 0, 1);OLED_ShowString(66, 0, "1(PA21)");
OLED_ShowMyCHinese(2, 2, 23);OLED_ShowMyCHinese(18, 2, 24);OLED_ShowString(34, 2, "S11");
OLED_ShowMyCHinese(2, 4, 21);OLED_ShowMyCHinese(18, 4, 22);OLED_ShowMyCHinese(34, 4, 0);OLED_ShowMyCHinese(50, 4, 1);OLED_ShowString(66, 4, "2(PA26)");
OLED_ShowMyCHinese(2, 6, 23);OLED_ShowMyCHinese(18, 6, 24);OLED_ShowString(34, 6, "S12");
//设置相关值
//通道1
ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;
freq1=MYCLK/ARR1;
CCR1=DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);
duty1=(double)CCR1/ARR1*100;
//通道2
ARR2=DL_TimerG_getLoadValue(PWM_2_INST)+1;
freq2=MYCLK/ARR2;
CCR2=DL_TimerG_getCaptureCompareValue(PWM_2_INST,GPIO_PWM_2_C0_IDX);
duty2=(double)CCR1/ARR2*100;
}


void page2(void)//通道1详情页
{
OLED_ShowMyCHinese(2, 0, 0);OLED_ShowMyCHinese(18, 0, 1);OLED_ShowString(34, 0, "1");
OLED_ShowMyCHinese(42, 0, 7);OLED_ShowMyCHinese(58, 0, 8);OLED_ShowString(74, 0, ":");
if(DL_Timer_isClockEnabled(PWM_1_INST)==true)
{
OLED_ShowMyCHinese(82, 0, 9);OLED_ShowMyCHinese(98, 0, 10);//9、10开启
}
if(DL_Timer_isClockEnabled(PWM_1_INST)==false)
{
OLED_ShowMyCHinese(82, 0, 11);OLED_ShowMyCHinese(98, 0, 12);//11、12关闭
}
//频率
OLED_ShowMyCHinese(2, 2, 2);OLED_ShowMyCHinese(18, 2, 3);OLED_ShowString(34, 2, ":");
ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;
freq1=(double)MYCLK/ARR1;
OLED_ShowNum(42,2,freq1,8,16);OLED_ShowString(106, 2, "Hz");

//占空比
OLED_ShowMyCHinese(2, 4, 4);OLED_ShowMyCHinese(18, 4, 5);OLED_ShowMyCHinese(34, 4, 6);OLED_ShowString(50, 4, ":");
CCR1=DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);
duty1=(double)CCR1/ARR1*100;
OLED_ShowFloatNum(58,4,duty1,3,4,16);OLED_ShowString(122, 4, "%");

}

void page3(void)//通道2详情页
{
OLED_ShowMyCHinese(2, 0, 0);OLED_ShowMyCHinese(18, 0, 1);OLED_ShowString(34, 0, "2");
OLED_ShowMyCHinese(42, 0, 7);OLED_ShowMyCHinese(58, 0, 8);OLED_ShowString(74, 0, ":");
if(DL_Timer_isClockEnabled(PWM_2_INST)==true)
{
OLED_ShowMyCHinese(82, 0, 9);OLED_ShowMyCHinese(98, 0, 10);//9、10开启
}
if(DL_Timer_isClockEnabled(PWM_2_INST)==false)
{
OLED_ShowMyCHinese(82, 0, 11);OLED_ShowMyCHinese(98, 0, 12);//11、12关闭
}

//频率
OLED_ShowMyCHinese(2, 2, 2);OLED_ShowMyCHinese(18, 2, 3);OLED_ShowString(34, 2, ":");
ARR2=DL_TimerG_getLoadValue(PWM_2_INST)+1;
freq2=(double)MYCLK/ARR2;
OLED_ShowNum(42,2,freq2,8,16);OLED_ShowString(106, 2, "Hz");

//占空比
OLED_ShowMyCHinese(2, 4, 4);OLED_ShowMyCHinese(18, 4, 5);OLED_ShowMyCHinese(34, 4, 6);OLED_ShowString(50, 4, ":");
CCR2=DL_TimerG_getCaptureCompareValue(PWM_2_INST,GPIO_PWM_2_C0_IDX);
duty2=(double)CCR2/ARR2*100;
OLED_ShowFloatNum(58,4,duty2,3,4,16);OLED_ShowString(122, 4, "%");

}


4.3 矩阵键盘

采用行输出(行扫描),列输入的方式获取键码并返回,基本与例程一致,需要行上拉输出。

int getKeyValue(void)
{
int h_arr[4] = {KEY_H1_PIN, KEY_H2_PIN, KEY_H3_PIN, KEY_H4_PIN};
int v_arr[4] = {KEY_V1_PIN, KEY_V2_PIN, KEY_V3_PIN, KEY_V4_PIN};
int i, j = 0;
int key_value = 0;
for (i = 0; i < 4; i++)
{
delay_cycles(1000);

DL_GPIO_setPins(KEY_PORT, h_arr[i]);
DL_GPIO_clearPins(KEY_PORT, h_arr[(i + 1) % 4]);
DL_GPIO_clearPins(KEY_PORT, h_arr[(i + 2) % 4]);
DL_GPIO_clearPins(KEY_PORT, h_arr[(i + 3) % 4]);

delay_cycles(100000);

for (j = 0; j < 4; j++)
{
if (DL_GPIO_readPins(KEY_PORT, v_arr[j]) != 0)
{
key_value = j * 4 + i + 1;
}
delay_cycles(1000);
}

delay_cycles(1000);
}

return key_value; // 没有按下,返回0
}

4.4 菜单操作实现

主页选项实现(第一级,主函数)

 //主函数
int main(void)
{
SYSCFG_DL_init(); // Initialize the device
DL_TimerG_startCounter(PWM_1_INST);
DL_TimerG_startCounter(PWM_2_INST);

NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);

DL_TimerG_startCounter(TIMER_0_INST);//开启定时中断

OLED_Init();
page0();
delay_cycles(32000000);
OLED_Clear();
while(1)
{
page1();
key_value1 = getKeyValue();
switch(key_value1)
{
case 11:while(getKeyValue()==11);channel1();break;
case 12:while(getKeyValue()==12);channel2();break;
default:if(key_value1!=0 & key_value1!=15){OLED_ShowString(2, 6, "INPUT ERROR!!!");delay_cycles(8000000);OLED_ShowString(2, 6, " ");}break;
}

}

}

通道1选项实现

void channel1()
{
int channel1_key_value1;
OLED_Clear();
page2();
while(1)
{
OLED_ShowMyCHinese(2,6,7);OLED_ShowString(18,6,":11");OLED_ShowMyCHinese(42,6,2);OLED_ShowString(58,6,":13");OLED_ShowMyCHinese(82,6,4);OLED_ShowString(98,6,":14");
channel1_key_value1 = getKeyValue();
switch(channel1_key_value1)
{
case 11:while(getKeyValue()==11);StartOrStop1();break;
case 13:while(getKeyValue()==13);changefreq1();break;
case 14:while(getKeyValue()==14){OLED_ShowString(2,6," ");OLED_ShowString(2,6,"SWITCH11 is '.'");}changeduty1();break;
case 15:while(getKeyValue()==15);OLED_Clear();return;
default:if(channel1_key_value1!=0 & channel1_key_value1!=11){OLED_ShowString(2, 6, "INPUT ERROR!!!");delay_cycles(8000000);OLED_ShowString(2, 6, " ");}break;

}
}
}

通道2选项实现

void channel2()
{
int channel2_key_value1;
OLED_Clear();
page3();
while(1)
{
OLED_ShowMyCHinese(2,6,7);OLED_ShowString(18,6,":11");OLED_ShowMyCHinese(42,6,2);OLED_ShowString(58,6,":13");OLED_ShowMyCHinese(82,6,4);OLED_ShowString(98,6,":14");
channel2_key_value1 = getKeyValue();
switch(channel2_key_value1)
{
case 12:while(getKeyValue()==12);StartOrStop2();break;
case 13:while(getKeyValue()==13);changefreq2();break;
case 14:while(getKeyValue()==14){OLED_ShowString(2,6," ");OLED_ShowString(2,6,"SWITCH11 is '.'");};changeduty2();break;
case 15:while(getKeyValue()==15);OLED_Clear();return;
default:if(channel2_key_value1!=0 & channel2_key_value1!=12){OLED_ShowString(2, 6, "INPUT ERROR!!!");delay_cycles(8000000);OLED_ShowString(2, 6, " ");}break;

}
}
}

调整通道一的频率和占空比

void changeduty1(void)
{
int Count1 = 0;
int Count2 = 0;
int key_staus = 0;
double pow1=0;
double temp1=0;
double temp2=0;
double result=0;
int key_value2 = 0;
while(1){
key_value2 = getKeyValue();
if(key_value2 != 0)
{
delay_cycles(640000);
if(key_value2==11) //如果S11按键按下,输入小数点
{
while(getKeyValue()!=0);//确保单击只输入一个值
// OLED_ShowString(58+(Count1)*8,4,"."); //更新显示
key_staus=1;
}
if((key_value2<=10) & (key_staus==0)) //标志位无效,S1~S10按键按下,输入整数部分值
{
while(getKeyValue()!=0);//确保单击只输入一个值
if(Count1<3) //如果输入次数小于3
{
temp1*=10; //总输入值左移一位
temp1+=key_value2%10; //获取一位输入
Count1++; //计次1加一
}
delay_cycles(640000);
OLED_ShowString(74,4,"0.0000"); //更新显示
OLED_ShowNum(58,4,temp1,3,16); //更新显示
}

if((key_value2<=10) & (key_staus==1)) //标志位有效,S1~S10按键按下,输入小数部分值
{
while(getKeyValue()!=0);//确保单击只输入一个值
if(Count2<4) //如果输入次数小于4
{
temp2*=10; //总输入值右移一位
temp2+=key_value2%10; //获取一位输入
Count2++; //计次2加一
}
delay_cycles(640000);
OLED_ShowString(90,4,"0000"); //更新显示
OLED_ShowNum(90,4,temp2,Count2,16); //更新显示
}


if(key_value2==16) //如果S16按键按下,确认
{
while(getKeyValue()!=0);//确保单击只输入一个值
OLED_ShowNum(0,6,temp1,4,16); //更新显示
OLED_ShowFloatNum(64,6,temp2/oled_pow(10,Count2),2,4,16); //更新显示
result = temp1 + (temp2/oled_pow(10,Count2));
OLED_ShowFloatNum(58,4,result,3,Count2,16); //更新显示
delay_cycles(640000);
if(result<=100) //如果占空比小于100%
{
duty1=result;
ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;;
CCR1=ARR1*duty1/100;
DL_TimerG_setCaptureCompareValue(PWM_1_INST,CCR1,GPIO_PWM_1_C0_IDX);//设置捕获比较值
result=0; //运算结果清零
Count1=0; //计次1清零
temp1=0; //中间值1清零
temp2=0; //中间值2清零
return;
}
else if(result>100) //否则
{
result=0; //运算结果清零
Count1=0; //计次1清零
temp1=0; //中间值1清零
temp2=0; //中间值2清零
OLED_ShowString(58,4," ERROR "); //更新显示
delay_cycles(16000000);//延时500ms
ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;
CCR1=DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);
duty1=(double)CCR1/ARR1*100;
OLED_ShowFloatNum(58,4,duty1,3,4,16); //更新显示
return;
}
}
if(key_value2==15) //如果S15按键按下,取消
{
while(getKeyValue()!=0);//确保单击只输入一个值
result=0; //运算结果清零
Count1=0; //计次1清零
temp1=0; //中间值1清零
temp2=0; //中间值2清零
OLED_ShowString(58,4," CANCEL "); //更新显示
delay_cycles(16000000);//延时500ms
ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;
CCR1=DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);
duty1=(double)CCR1/ARR1*100;
OLED_ShowFloatNum(58,4,duty1,3,4,16); //更新显示
return;
}
}
}
return;
}

void changefreq1(void)//具体调频率

{

//int temp2=key_value2;

int Count = 0;

uint32_t temp1=0;

double temp2=0;

int key_value2 = 0;

while(1){

key_value2 = getKeyValue();

if(key_value2 != 0)

{

delay_cycles(640000);

if(key_value2<=10) //如果S1~S10按键按下,输入值

{

while(getKeyValue()!=0);//确保单击只输入一个值

if(Count<7) //如果输入次数小于7

{

temp1*=10; //总输入值左移一位

temp1+=key_value2%10; //获取一位输入

Count++; //计次加一

}

delay_cycles(640000);

OLED_ShowNum(42,2,temp1,8,16); //更新显示

}

if(key_value2==16) //如果S16按键按下,确认

{

while(getKeyValue()!=0);//确保单击只输入一个值

if(temp1<=1000000) //如果频率小于等于1MHz

{

ARR1=DL_Timer_getLoadValue(PWM_1_INST)+1;

CCR1=DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);

duty1=(double)CCR1/ARR1*100;

freq1=temp1;

ARR1=MYCLK/freq1+1;

CCR1=MYCLK/freq1*duty1/100;//调整频率的同时,尽量保证占空比不发生变化

DL_TimerG_setCaptureCompareValue(PWM_1_INST,CCR1,GPIO_PWM_1_C0_IDX);//设置捕获比较值

DL_Timer_setLoadValue(PWM_1_INST,ARR1-1);

OLED_ShowNum(42,2,freq1,8,16); //更新显示

ARR1=DL_Timer_getLoadValue(PWM_1_INST)+1;

CCR1=DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);

duty1=(double)CCR1/ARR1*100;

OLED_ShowFloatNum(58,4,duty1,3,4,16); //更新显示

temp1=0; //中间值清零

Count=0; //计次清零

return;

}

else if(temp1>1000000) //否则

{

Count=0; //计次清零

temp1=0; //中间值清零

OLED_ShowString(42,2," ERROR "); //更新显示

delay_cycles(16000000);//延时500ms

ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;

freq1=MYCLK/ARR1;

OLED_ShowNum(42,2,freq1,8,16);

return;

}

}

if(key_value2==15) //如果S15按键按下,取消

{

while(getKeyValue()!=0);//确保单击只输入一个值

Count=0; //计次清零

temp1=0; //中间值清零

OLED_ShowString(42,2," CANCEL "); //更新显示

delay_cycles(16000000);//延时500ms

ARR1=DL_TimerG_getLoadValue(PWM_1_INST)+1;

freq1=MYCLK/ARR1;

OLED_ShowNum(42,2,freq1,8,16);

return;

}





}

}

return;

}

调整通道二同理,只是改了函数名和变量而已,就不列出来了


调整PWM输出状态,通过操作PWM的时钟开关来实现对PWM波输出与否的控制


void StartOrStop1(void)
{
delay_cycles(640000);
if(DL_Timer_isClockEnabled(PWM_1_INST)==false)
{
DL_Timer_enableClock(PWM_1_INST);
OLED_ShowMyCHinese(82, 0, 9);OLED_ShowMyCHinese(98, 0, 10);//9、10开启
delay_cycles(640000);
}
else if(DL_Timer_isClockEnabled(PWM_1_INST)==true)
{
DL_Timer_disableClock(PWM_1_INST);
OLED_ShowMyCHinese(82, 0, 11);OLED_ShowMyCHinese(98, 0, 12);//11、12关闭
delay_cycles(640000);
}

}





5 遇到的主要难题

5.1 定时中断

通过定时中断定时每一秒发送相应信息

void TIMER_0_INST_IRQHandler()
{
uint32_t ch1_ARR = DL_Timer_getLoadValue(PWM_1_INST)+1;
uint32_t ch1_CCR = DL_TimerG_getCaptureCompareValue(PWM_1_INST,GPIO_PWM_1_C0_IDX);
double ch1_duty = (double)ch1_CCR/ch1_ARR*100;
uint32_t ch1_freq = MYCLK/(ch1_ARR-1);


uint32_t ch2_ARR = DL_Timer_getLoadValue(PWM_2_INST)+1;
uint32_t ch2_CCR = DL_TimerG_getCaptureCompareValue(PWM_2_INST,GPIO_PWM_2_C0_IDX);
double ch2_duty = (double)ch2_CCR/ch2_ARR*100;
uint32_t ch2_freq = MYCLK/(ch2_ARR-1);

Serial_SendString("CH1:freq= ");Serial_SendNumber(ch1_freq, 7);Serial_SendString(" Hz ");Serial_SendString("duty= "); Serial_SendFloatNum(ch1_duty, 3, 6);Serial_SendString("%\r\n");
Serial_SendString("CH2:freq= ");Serial_SendNumber(ch2_freq, 7);Serial_SendString(" Hz ");Serial_SendString("duty= "); Serial_SendFloatNum(ch2_duty, 3, 6);Serial_SendString("%\r\n");


}


5.2 串口输出

将每个字节的发送“DL_UART_Main_transmitData(UART_1_INST, Byte); /* 等待数据传输完毕 */while (DL_UART_Main_isBusy(UART_1_INST));”封装进各类字符串和数字发送

//一系列串口函数
void Serial_SendByte(uint8_t Byte)
{
/* 发送一个数据 */
DL_UART_Main_transmitData(UART_1_INST, Byte);
/* 等待数据传输完毕 */
while (DL_UART_Main_isBusy(UART_1_INST));
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Array[i]);
}
}

void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}

void Serial_SendFloatNum(double Number, uint8_t IntLength, uint8_t FraLength)
{
uint32_t PowNum, IntNum, FraNum;

/*提取整数部分和小数部分*/
IntNum = Number; //直接赋值给整型变量,提取整数
Number -= IntNum; //将Number的整数减掉,防止之后将小数乘到整数时因数过大造成错误
PowNum = oled_pow(10, FraLength); //根据指定小数的位数,确定乘数
FraNum = round(Number * PowNum); //将小数乘到整数,同时四舍五入,避免显示误差
IntNum += FraNum / PowNum; //若四舍五入造成了进位,则需要再加给整数

/*显示整数部分*/
Serial_SendNumber(IntNum,IntLength);

/*显示小数点*/
Serial_SendString(".");

/*显示小数部分*/
Serial_SendNumber(FraNum,FraLength);
}

5.3 浮点数输入、传输和输出

浮点输入的重点在于记录小数点的输入时机,从而计算相应数值

浮点传输和输出的重点在于分别输出整数部分、小数点和小数部分

6 未来的计划建议

矩阵键盘和OLED显示目前还有一点小bug,除此之外,调整频率的时候占空比会变化的问题还没有解决,OLED显示不够方便,显示的UI过于简陋,作者正在赶工改善中……

附件下载
基于EVM_MSPM0L1306平台实现的简易双通道PWM发生器.rar
团队介绍
团队就我一个人,来自桂林电子科技大学的一名电子爱好者,可以叫我卤蛋,卤蛋必将自强不息,砥砺前行!
团队成员
彭晨峰
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号