51单片机控制信号发生器源程序(正弦波、方波)
2020-08-24 来源:51hei
单片机型号:STC15W4K32S4
数字编码器EC11作为控制波形、频率、占空比的开关
液晶1602A作为显示信息
单片机源程序如下:
Note:
2020-05-06 Update
1. Fix an error in main funciton, cause the EC11 Encoder rotate disorder.
2. Fix an error of interface display during switch frequency.
2020-05-24 Update
1. Fix precision of PWM frequency
2. Fix the information error in interface
波形发生器
作者:老刘爱捣鼓
版本:V0.3.1
单片机型号:STC15W4K32S4 @24MHz
B站视频教程:https://www.bilibili.com/video/BV12k4y197Qu
老刘爱捣鼓(全网同名)期待您的关注!
规格:
输出:单通道
方波:1Hz~4MHz
正弦波:1Hz-10kHz
波幅:约等于VCC,5V左右
带载能力:无带载能力
显示屏:LCD1602
控制:EC11编码器
界面:
左下角显示波形图标(方波/正弦波)和输出状态(On/OFF)
F:频率
D:方波占空比
CD:时钟分频系数(For information only)
P:用于生成正弦波的PWM频率(For information only)
Pt:用于生成正弦波的点数(For information only)
操作:
单击编码器:方波界面下,切换频率和占空比
双击编码器:开启或关闭波形输出
长按编码器:切换方波界面/正弦波界面/电压显示界面
旋转编码器:调节参数
2020-05-06 更新
1. 修正main函数循环中会引起编码器旋转混乱的错误
将main函数中while循环中的Update_Flag=0;放到前面清零,放在后面会在编码器高速旋转时,多次触发中断而Update_Flag在最后被清零则无法正确更新LCD
2. 修正在切换频率时,界面的显示错误
2020-05-24 Update
1. 修正PWM频率精度
2. 修正界面信息的显示错误
*/
#include #include #include 'lcd1602.h' #include 'wave.h' #include 'settings.h' #include 'delay.h' #include 'config_stc.h' //#include 'uart.h' //#include 'stdio.h' #ifndef uint8 #define uint8 unsigned char #endif #ifndef int8 #define int8 char #endif #ifndef uint16 #define uint16 unsigned int #endif #ifndef uint32 #define uint32 unsigned long int #endif #define TIMER_0 1 //定时器0中断序号 #define INT_1 2 //编码器旋转 触发外部中断 #define INT_0 0 //编码器按下 触发外部中断 uint8 Timer0_Count; bit Update_Flag = 1; void main(void) { //LCD Pin P1M1 &= 0x00; //设置P1口为准双向 P1M0 &= 0x00; //设置P1口为准双向 P0M1 &= 0x00; //设置P0口为准双向 P0M0 &= 0x00; //设置P0口为准双向 //信号输出Pin PWM3 = 0; //设置PWM3 P4.5低电平 PWM4 = 0; //设置PWM4 P4.4低电平 P4M1 |= 0x30; //设置P4.4(PWM4_2),4.5(PWM3_2)为高阻 P4M0 &= ~0x30; //设置P4.4(PWM4_2),4.5(PWM3_2)为高阻 /* 编码器旋转中断 Interrupt for Encoder Rotation */ IT1 = 0; //外部中断1触发方式,上升沿和下降沿 PX1 = 1; //外部中断1高优先级 EX1 = 1; //开启外部中断1 /* 编码器按键中断 Interrupt for Encoder Click */ IT0 = 1; //外部中断0触发方式,下降沿 PX0 = 1; //外部中断0高优先级 EX0 = 1; //开启外部中断0 /* 定时器0,用于更新电压信息计时 Timer 0 for updating the information of VCC*/ TMOD &= 0xF0; //设置定时器0模式 16位自动重载,在Keil中debug的话,请注意,这种设置是8051的旧13位模式 AUXR &= ~0x80; //定时器0时钟12T模式 TL0 = 0xC0; //设置定时初值 24MHz 20ms TH0 = 0x63; //设置定时初值 24MHz 20ms ET0 = 1; //允许T0溢出中断 /* 定时器1,用于生成小于50Hz的PWM Timer 1 for generate the PWM when frequency less than 50Hz*/ TMOD &= 0x0F; //工作模式,0: 16位自动重装 AUXR &= ~0x40; //12T ET1 = 1; //允许中断 EA = 1; //开总中断 //UartInit(); //UartInit_interrupt(); PWM_Hz_Pre = PWM_Hz; Wave_Shape_Pre = Wave_Shape; Get_PWM_Duty_Limit(); if (PWM_Duty > PWM_Max_Duty) PWM_Duty = PWM_Max_Duty; else if (PWM_Duty < PWM_Min_Duty) PWM_Duty = PWM_Min_Duty; Lcd_Init(); while (1) { if (Update_Flag) { /* Update_Flag要马上清零,如果放在Update_LCD后面,会造成假如Update_LCD的过程中再次触发编码器旋转中断的话, 在执行完Update_LCD后,在中断中置位的Update_Flag却被清零了,造成LCD没刷新,输出和显示的不一致。 另一种方法是在执行Update_LCD前把中断关掉,执行完再打开中断, 但如果这里面的函数执行时间比较长,会产生旋转触发不及时,使用感受卡顿。 关中断来避免重复触发是有好处的,可以避免在函数执行过程中多次触发将参数修改掉, 函数执行过程中参数被修改,可能会造成计算结果混乱,严重时会造成溢出,甚至单片机复位死机。 */ Update_Flag = 0; Wave_OFF(); if (Wave_Shape_Pre != Wave_Shape) { Wave_Shape_Pre = Wave_Shape; if (Wave_Shape == 0) { EX1 = 0; //关闭外部中断1(编码器旋转) TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 (定时器0为VCC更新计时) } else if (Wave_Shape == 1) { PWM_Hz = PWM_Hz_Pre; EX1 = 1; //开启外部中断1(编码器旋转) TR0 = 0; //关闭定时器0 (定时器0为VCC更新计时) TF0 = 0; //清除TF0标志 } else if (Wave_Shape == 2) { PWM_Hz_Pre = PWM_Hz; } } if (Wave_Shape == 1) //方波 { Set_PWMCKS_PS(); Set_PWM_Cycle(); Set_PWM_Width(); } else if (Wave_Shape == 2) //正弦波 { Set_Sin_Table_Times(); Set_PWMCKS_PS(); Set_PWM_Cycle(); } Update_LCD(); //EX1 = 0; Set_Wave_Shape(); //IE1=0; //EX1 = 1; } } } /* 编码器旋转响应函数 Encoder Rotate */ void Scan_EC11(void) { /* 正转 Rotate clockwise */ if ((EC11_A != EC11_B)) { Change_Val(1); } /* 反转 Rotate anticlockwise*/ else if ((EC11_A == EC11_B)) { Change_Val(0); } } /* 编码器旋转中断 Interrupt for Encoder rotation */ void INT1_interrupt(void) interrupt INT_1 { Delay1ms(); Scan_EC11(); Update_Flag = 1; //Delay50ms(); IE1 = 0; } /* 编码器点击中断 Interrupt for Encoder click */ void INT0_interrupt(void) interrupt INT_0 { Delay5ms(); if (!EC11_KEY) { /* 长按 Long Press */ if (Delay500ms_long_click()) { Wave_Shape++; if (Wave_Shape > WAVE_NUM) Wave_Shape = 0; if (Wave_Shape == 2) Options = 1; WAVE_ON = 0; Clear_LCD_Flag = 1; } /* 双击 Double click */ else if (Delay200ms_double_click()) { if (Wave_Shape > 0) { WAVE_ON = ~WAVE_ON; } } /* 单击 Single click */ else { if (Wave_Shape == 1) Options = ~Options; } Update_Flag = 1; } Delay5ms(); IE0 = 0; } /* 更新电压信息计时中断 Timer interrupt for update voltage information */ void TIMER0_interrupt() interrupt TIMER_0 { if (++Timer0_Count > 200) //200x20=4000ms { Timer0_Count = 0; Update_Flag = 1; } }
上一篇:电阻、电容和电感测试仪设计