本科学生单片机课程设计报告
题 目 数控直流稳压电源设计
院 (系) 工程与设计学院
专业、年级 电子信息工程 2019级
课程设计成绩评定表
项目 | 评分依据 | 满分 | 得分 |
设计作品 | 电路板焊接工艺 | 10 | |
功能完整 | 20 | ||
性能指标 | 10 | ||
程序运行可靠 | 20 | ||
人机交互友好,操作方便 | 10 | ||
设计报告 | 内容完整 | 10 | |
写作规范 | 10 | ||
设计报告篇幅符合要求 | 10 |
实评总分 指导教师签名
目录
1.设计要求
用单片机、D/A(或数字电位器)、A/D及串联稳压电路设计数控直流稳压电源。性能指标:
用LED数码管或LCD显示设定电压值及实测输出电压值;
电压调节范围:0V—20V,按0.1V步进设置,额定输出电流1A;
稳压精度:负载调整率(0-1A)≤5%,电压调整率(1-10V压降)≤5%
2.硬件电路设计
2.1.设计思路
主控芯片选择STC15W4K48S4,单片机供电系统选择7805作为稳压芯片,通过对24V输入电压进行稳压调整,输出 5V作为电路的供电系统,之后串联稳压部分选择LM358作为电压比较器,并且选择三极管TIP41C作为调整管使用。本设计通过单片机内部AD对实时电压的测量与实际设定值进行比对,PID闭环增益调节PWM输出值,显示模块运用了0.96寸中景园的OLED进行显示。
2.2.单片机供电系统电路
本设计采用的供电系统电路为L7805构成的5V稳压电路
L7805是常用的三端稳压器件,顾名思义05就是输出电压为5v,7805输出波纹很小,用来给单片机供电。使用方便,在这里用的是TO-220封装。
2.3.串联稳压模块电路
串联稳压电路中,选用了LM358作为比较器,集成运算放大器具有开环增益高和输出阻抗低等特点,用它做稳压电源中的比较放大器是比较理想的。在这里,将单片机的输出作为比较电压,取RP2两端电压作为实际电压值,通过两个电压值经过比较器,再通过三极管TIP41C,这里选取TIP41C作为调整管。
LM358是双运算放大器。内部包括有两个独立的、高增益、内部频率补偿的运算放大器,适合于电源电压范围很宽的单电源使用,也适用于双电源工作模式,在推荐的工作条件下,电源电流与电源电压无关。共模输入范围包括负电源,因而消除了在许多应用场合中采用外部偏置元件的必要性。
电源电压范围宽:单电源(3-30V),单位增益频带宽(约1MHz)
TIP41C作为调整管,有以下特点最大平均电流:6A,最大峰值电流:10A,最大Ice漏电流:700μA(@Vce=60V,Vbe=0),最大Vcbo=100V,最大Vceo=100V,最大Vbe=5V,最高有效结温:最大值150摄氏度,封装形式:直插封装TO-22,主要用途:适用于电子开关线路主要特点:功率大、驱动电流大,所以在这里并不需要选择组合管,只用TIP41C即可满足需要。
2.4.π型LC滤波电路及ADC采样电路
电感滤波电路是用电感器构成的一种滤波电路,其滤波效果相当好,只是要求滤波电感的电感量较大,电路的成本比较高。电路中常使用π型LC滤波电路。L1滤波电感对交流感抗大对直流电阻小,这里电容选取470uf耐压为63V,大于22V电源电压的2½倍
从p1.0口为ADC0输入口进行采样转换,考虑到单片机本身带有内阻,这里对于采样电路进行隔离处理,类似于负载就不会影响到串联稳压电路的增益。
2.5单片机及下载电路
选型STC15W4K48S4,单片机自带八通道十位ADC
MCU封装LQFP44
数据手册给出的下载电路
实际中将P3.1和P3.0引出作为下载口
2.6显示部分电路
采用中景园电子生产的0.96尺寸的OLED,内部自带线与,单片机通过IIC总线控制,时序按照数据手册的时序图编写
3.程序设计
3.1.程序流程图
先进行定时器初始化设置,以及OLED以及的初始化设置,程序设计总思路就是先按键设置好所需要的目标电压值,先按照原先设定的占空比生成PWM波输出,得到此时的输出电压值,同时AD采样得到实时电压值,与设定电压值进行比对,实际输出电压大的话,会减小输出PWM波的占空比,实际输出电压小的话则相反。
3.1.1 pwm波生成部分
先对定时器进行初始化,使用的是定时器0的方式2,通过中断进行pwm波生成,pwm波产生部分程序如下:
void LoadPWM(u16 i) //计算PWM重装值函数
{
u16 j;
if(i > PWM_HIGH_MAX) i = PWM_HIGH_MAX; //如果写入大于最大占空比数据,则强制为最大占空比。
if(i < PWM_HIGH_MIN) i = PWM_HIGH_MIN; //如果写入小于最小占空比数据,则强制为最小占空比。
j = 65536UL - PWM_DUTY + i; //计算PWM低电平时间
i = 65536UL - i; //计算PWM高电平时间
EA = 0;
PWM_high = i; //装载PWM高电平时间
PWM_low = j; //装载PWM低电平时间
EA = 1;
}
3.1.2 OLED显示
先对OLED进行初始化设置,设置正常显示,分别显示的是设定电压,以及此时实时电压,和PWM占空比,通过取模软件取四个汉子字库数组用来中文显示调用,部分显示程序如下:
OLED_Init();//初始化OLED
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
sprintf(str,"DUTY=%0.2f%c ",((float)pwm/(float)PWM_DUTY)*100,'%');
OLED_ShowString(0,0,str,16);
sprintf(str,":%0.3f V ",set_vol);
OLED_ShowString(n*2,4,str,16);
sprintf(str,":%0.3f V ",vol);
OLED_ShowString(n*2,2,str,16);
void init_system(void) //OLED屏幕开启初始化
{
do
{
low_voltage();
LoadPWM(pwm);
adc_v=Get_ADC10bitResult_cont(ADC_CH0,5); //ADC0获得采样值
vol=(float)adc_v*5/1023; //10位ADC,基准5V电压
vol_set();
sprintf(str,"%d",num);
OLED_ShowString(0,4,str,16);
num++;
OLED_ShowString(0,0,"loading. ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading.. ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading... ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading.... ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading..... ",16);
delay_ms(200);
}while(vol>init_vol&&num<8);
}
//显示汉字
void OLED_ShowChinese(u8 x,u8 y,u8 no,u8 sizey)
{
u16 i,size1=(sizey/8+((sizey%8)?1:0))*sizey;
for(i=0;i<size1;i++)
{
if(i%sizey==0) OLED_Set_Pos(x,y++);
if(sizey==16) OLED_WR_Byte(Hzk[no][i],OLED_DATA);//16x16字号
// else if(sizey==xx) OLED_WR_Byte(xxx[c][i],OLED_DATA);//用户添加字号
else return;
}
}
3.1.3 AD采样
采用单片机内部ADC0,在配置完成ADC0的端口后调用ADC值获取函数,通道选用0,采样五次取平均值,得到的值根据十位ADC位数和基准电压进行转换,部分程序如下:
adc_v=Get_ADC10bitResult_cont(ADC_CH0,5); //ADC0获得采样值
vol=(float)adc_v*5/1023; //10位ADC,基准5V电压
u16 Get_ADC10bitResult_cont(u8 channel,u16 count) //均值滤波
{
int temp;
int i=count;
do
{
temp+=Get_ADC10bitResult(channel);
i--;
}while(i>0);
return (temp/count);
}
u16 Get_ADC10bitResult(u8 channel) //channel = 0~7
{
u16 adc;
u8 i;
if(channel > ADC_CH7) return 1024; //错误,返回1024,调用的程序判断
ADC_RES = 0;
ADC_RESL = 0;
ADC_CONTR = (ADC_CONTR & 0xe0) | ADC_START | channel;
NOP(4); //对ADC_CONTR操作后要4T之后才能访问
for(i=0; i<250; i++) //超时
{
if(ADC_CONTR & ADC_FLAG)
{
ADC_CONTR &= ~ADC_FLAG;
if(PCON2 & (1<<5)) //10位AD结果的高2位放ADC_RES的低2位,低8位在ADC_RESL。
{
adc = (u16)(ADC_RES & 3);
adc = (adc << 8) | ADC_RESL;
}
else //10位AD结果的高8位放ADC_RES,低2位在ADC_RESL的低2位。
{
adc = (u16)ADC_RES;
adc = (adc << 2) | (ADC_RESL & 3);
}
return adc;
}
}
return 1024; //错误,返回1024,调用的程序判断
}
3.1.4按键部分
设定两个按键来对于设定电压进行加减,步进为0.1V,支持连续加减,期间进行延迟软消抖,部分程序如下:
switch(KEY_Scan(1)) //两个按键扫描,分别用来加减设定电压
{
case KEY0_PRES:
set_vol+=0.1;
if(set_vol>20) set_vol=20;
break;
case KEY1_PRES:
set_vol-=0.1;
if(set_vol<0) set_vol=0;
break;
}
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0))
{
// delay_ms(20);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
}else if(KEY0==1&&KEY1==1)key_up=1;
return 0;// 无按键按下
}
3.1.5主控部分
主控部分分为低电压1V一下设定十个点的PWM值实现,高电压部分>1V部分通过实时读取AD采样得到的数据,通过计算AD数据与实际电压之间的差值作为调制系数,用调制系数来对于PWM值进行调整从而改变其高低电平持续的时间,来达到PID改变占空比的算法实现。
void low_voltage(void)
{
PWM_DUTY=27000; // 定义PWM的周期,数值为时钟周期数
//1V以下采用PWM频率为1K,pwm数值一定的时候输出电压更低更接近与所需要的1V以下的电压
if(set_vol<0.05) //设定值为0的处理
{
TR0=0;
P35=0;
}
else
{
EA = 1;
TR0=1;
}
if(set_vol>0.05&&set_vol<0.15) pwm=32; //1V内的PWM设定
if(set_vol>0.15&&set_vol<0.25) pwm=69;
if(set_vol>0.25&&set_vol<0.35) pwm=105;
if(set_vol>0.35&&set_vol<0.45) pwm=140;
if(set_vol>0.45&&set_vol<0.55) pwm=176;
if(set_vol>0.55&&set_vol<0.65) pwm=212;
if(set_vol>0.65&&set_vol<0.75) pwm=247;
if(set_vol>0.75&&set_vol<0.85) pwm=282;
if(set_vol>0.85&&set_vol<0.95) pwm=318;
if(set_vol>0.95&&set_vol<1.05) pwm=355;
}
void high_voltage(void)
{
PWM_DUTY=2700; //1V以上采用PWM频率为10K
EA = 1;
TR0=1;
if(set_vol<=1.15)
{
pwm=114;
}
temp=(set_vol-vol)*p;
if(pwm-temp>32&&pwm+temp<2700) pwm+=temp; //增量式PID
if(vol<set_vol) pwm++; //比例调节
if(vol>set_vol) pwm--;
if(pwm>PWM_DUTY) pwm=PWM_DUTY; //设定PWM的上限
if(pwm<50) pwm=50; //设定PWM的下限
}
PCB顶层
PCB底层
3D预览视图
5.实验结果图
测量单片机输出PWM波形:
最低到0V的时候:
接入25.8欧姆的负载,输出电流为1.01A
6.总结
通过本次课程设计学到了很多东西,最初刚选好课题然后在咨询老师之后,有了初步的设计思路,再到后来的万用板验证,最后PCB画板打板,再一次次焊接测试然后进行电路的改进和原理上的验证,中间经历大概两三个月的时间,有过MCU烧毁时的失落也有一步步尝试改进后来取得进步的欣慰。这次课设作品相比于之前的电赛和平时自己用的最小系统板,第一次尝试MCU裸片的应用的确存在很大的挑战,幸得可以在STC的数据手册上去学习STC15的开发电路,在STC15单片机程序的编写上面也得到很大的帮助。
在刚开始调电路时发现总是达不到0V,后来通过示波器测PWM波形发现其PWM波在占空比过小的情况下会出现明显的抖动和失真现象,这是在之前使用STM32单片机所从未遇到的情况,参考数据手册在得知其PWM的性能与其频率和频率有关后,对于PWM频率选定为1K(用于做1V以下的部分),10k(用于做1V以上的部分)发现效果可以达到题目要求,在屏幕的显示上最初想使用串口屏,因其传输速度较快且占用IO口较少,但后来因为硬件电路修改花时间较长,没能来得及学习串口屏的配置和相关编写软件的使用,改用之前用过的中景园IIC的OLED屏幕,串口屏准备在暑假备赛电赛期间学好用熟。
通过此次课设再一次温习了很多模电的相关知识,为了验证也焊接了许多块万用板和自己画的PCB板,动手焊接能力得到了锻炼,学到了很多东西,期间遇到过一些模电方面的问题——输出端的电流总是上不去,咨询老师后也进行了相关的改进,非常感谢杨老师给予的帮助和指导以及能够有这样一次锻炼的机会。
7.附录:程序
#include "mycode.h"
#define magn0 4.8
#define magn1 4.56 //2.5
#define magn2 4.65
#define magn3 4.7
#define p 5
#define n 16
#define init_vol 3.0
sbit ADC1 = P1^4;
u16 pwm; //定义PWM输出高电平的时间的变量。用户操作PWM的变量。
u16 adc_v;
float vol;
float set_vol;
int temp;
char str[16];
int num=0;
void my_init(void);
void vol_set(void);
void low_voltage(void);
void high_voltage(void);
void init_system(void);
void main(void)
{
my_init();
// UART_config();
GPIO_LEDconfig();
GPIO_ADCconfig();
GPIO_KEYconfig();
ADC_config();
OLED_Init();//初始化OLED
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
EA = 0;
set_vol=0.0;
pwm=32;
LoadPWM(pwm);
TR0=0;
P35=0;
low_voltage();
LoadPWM(pwm);
init_system();
OLED_ShowChinese(0,2,7,16);
OLED_ShowChinese(n,2,9,16);
OLED_ShowChinese(0,4,10,16);
OLED_ShowChinese(n,4,11,16);
while (1)
{
adc_v=Get_ADC10bitResult_cont(ADC_CH0,5); //ADC0获得采样值
vol=(float)adc_v*5/1023; //10位ADC,基准5V电压
vol_set();
switch(KEY_Scan(1)) //两个按键扫描,分别用来加减设定电压
{
case KEY0_PRES:
set_vol+=0.1;
if(set_vol>20) set_vol=20;
break;
case KEY1_PRES:
set_vol-=0.1;
if(set_vol<0) set_vol=0;
break;
}
if(set_vol<=1.0)
{
low_voltage();
}
else
{
high_voltage();
}
LoadPWM(pwm);
sprintf(str,"DUTY=%0.2f%c ",((float)pwm/(float)PWM_DUTY)*100,'%');
OLED_ShowString(0,0,str,16);
sprintf(str,":%0.3f V ",set_vol);
OLED_ShowString(n*2,4,str,16);
sprintf(str,":%0.3f V ",vol);
OLED_ShowString(n*2,2,str,16);
}
}
void my_init(void)
{
P_PWM = 0;
P3M1 &= ~(1 << 5); //P3.5 设置为推挽输出
P3M0 |= (1 << 5);
Timer_config();
}
void low_voltage(void)
{
PWM_DUTY=27000; // 定义PWM的周期,数值为时钟周期数
//1V以下采用PWM频率为1K,pwm数值一定的时候输出电压更低更接近与所需要的1V以下的电压
if(set_vol<0.05) //设定值为0的处理
{
TR0=0;
P35=0;
}
else
{
EA = 1;
TR0=1;
}
if(set_vol>0.05&&set_vol<0.15) pwm=32; //1V内的PWM设定
if(set_vol>0.15&&set_vol<0.25) pwm=69;
if(set_vol>0.25&&set_vol<0.35) pwm=105;
if(set_vol>0.35&&set_vol<0.45) pwm=140;
if(set_vol>0.45&&set_vol<0.55) pwm=176;
if(set_vol>0.55&&set_vol<0.65) pwm=212;
if(set_vol>0.65&&set_vol<0.75) pwm=247;
if(set_vol>0.75&&set_vol<0.85) pwm=282;
if(set_vol>0.85&&set_vol<0.95) pwm=318;
if(set_vol>0.95&&set_vol<1.05) pwm=355;
}
void high_voltage(void)
{
PWM_DUTY=2700; //1V以上采用PWM频率为10K
EA = 1;
TR0=1;
if(set_vol<=1.15)
{
pwm=114;
}
temp=(set_vol-vol)*p;
if(pwm-temp>32&&pwm+temp<2700) pwm+=temp; //增量式PID
if(vol<set_vol) pwm++; //比例调节
if(vol>set_vol) pwm--;
if(pwm>PWM_DUTY) pwm=PWM_DUTY; //设定PWM的上限
if(pwm<50) pwm=50; //设定PWM的下限
}
void vol_set(void)
{
if(set_vol<2.5) vol=vol*magn1;
if(set_vol>=2.5&&set_vol<=4.50) vol=vol*magn2;
if(set_vol>=4.5&&set_vol<=14.1)
{
vol=vol*magn2;
vol+=vol*0.01;
}
if(set_vol>14.1)
{
vol=vol*magn3;
vol+=vol*0.005;
}
}
void init_system(void) //OLED屏幕开启初始化
{
do
{
low_voltage();
LoadPWM(pwm);
adc_v=Get_ADC10bitResult_cont(ADC_CH0,5); //ADC0获得采样值
vol=(float)adc_v*5/1023; //10位ADC,基准5V电压
vol_set();
sprintf(str,"%d",num);
OLED_ShowString(0,4,str,16);
num++;
OLED_ShowString(0,0,"loading. ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading.. ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading... ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading.... ",16);
delay_ms(200);
OLED_ShowString(0,0,"loading..... ",16);
delay_ms(200);
}while(vol>init_vol&&num<8);