前段时间买了两片STC12C5A60S2来玩,手头又有3R33,遂准备搭个可调电源,带电压电流指示的。
STC12C5A60S2有60K的程序空间啊,不跑个RTOS实在对不起这配置,所以软件上就选择了RTX51 TINY作为操作系统,这样编程上方便了N倍!硬件上采用TL431A作为基准,而不使用芯片的Vcc,这样加上电流,电压,总共需要3路的AD转换,算法很简单,就是先用TL431的2.5V基准(后面测出来是2.45V的,不知是万用表还是器件的问题)算出电源电压Vcc,然后再求出另外两路的电压,软件滤波算法如下:
unsigned int GetADCResult(unsigned char ch)
{
unsigned int AD_result;
ADC_RES = 0;
ADC_RESL = 0;
ADC_CONTR = ADC_POWER | ADC_SPEEDL | ch | ADC_START;
_nop_();
_nop_();
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG));
ADC_CONTR &= ~ADC_FLAG;
AD_result = ADC_RES;
AD_result <<= 2;
AD_result += ADC_RESL;
return AD_result;
}
unsigned int GetResult(unsigned char ch)
{
unsigned int ADC_min, ADC_max, ADC_tmp, ADC_result, ADC;
unsigned char i, j;
ADC = 0;
for(j = 0; j < 16; j++)
{
ADC_result = 0;
ADC_min = ADC_max = GetADCResult(ch);
for(i = 0; i < 8; i++)
{
ADC_tmp = GetADCResult(ch);
if(ADC_tmp < ADC_min)
{
ADC_result += ADC_min;
ADC_min = ADC_tmp;
}
else if(ADC_tmp > ADC_max)
{
ADC_result += ADC_max;
ADC_max = ADC_tmp;
}
else
{
ADC_result += ADC_tmp;
}
}
ADC_result /= 8;
ADC += ADC_result;
}
ADC /= 16;
return ADC;
}
测试后,发现单片机还有很多的空闲时间在打瞌睡(外接了一个LED,当系统空闲是就电亮),
在CONF_TNY.A51文件里面修改一下这个部分
CPU_IDLE MACRO
MOV P1,#0EFH ;进入空闲模式时点亮LED
ORL PCON,#1 ; set 8051 CPU to IDLE
MOV P1,#0FFH ;离开空闲模式时熄灭LED
ENDM
所以就把采样次数取得很高,发现STC12C5A60S2性能还是很强的,在RTX51上跑了3个任务,分别是读取计算3路的AD、读取温度、1602LCD显示,单片机依旧有很多的时间在打瞌睡。把采用次数设置为2的倍数,可以优化CPU的性能,编译器直接用位运算取代乘除运算。
上图!
用洞洞板搭了一个3R33可调电源,然后输出电压用3枚51K的电阻取了1/3给STC12C5A60S2的一路AD,测量范围为0~15V,三枚电阻有配过,测出来都是50.7K,电压取样电路到单片机AD引脚的连接要严格按照STC手册的,这样测出来的电压才是稳定的,不会飘。
测出来都是5.07V,TL431A的基准取2.45V,之前用2.5V,发现和万用表测出来的电压偏差大,后面取为2.45就一致了
调压用的电位器用多圈的,考虑到电压不需要经常调,没用大的那种。
这是开发板,看见那颗亮着的红色LED没,它说单片机现在正在睡觉
发现开发板还是不错的,调试好后,做个系统板,把单片机,1602统统搞过去,再用杜邦线连一下(PS:作者很喜欢杜邦线),就OK了。
电流检测电路
http://bbs.mydigit.cn/read.php?tid=484474 源码如下(不包含温度检测)PS:RTX51tiny要禁止任务循环,具体查看手册
#include <STC12C5A.H>
#include <rtx51tny.h>
#include <intrins.h>
#include <stdio.h>
sbit rs=P2^3;//H数据,L命令
sbit lcden=P2^5;//高脉冲使能,使能脉宽最小150ns
sbit rw=P2^4;
unsigned int value;
float ADC_v = 0;
float ADC_i = 0;
/******************************************************************************/
void lcddelay(unsigned char x)
{
unsigned int y;
for(;x>0;x--)
{
for(y=100;y>0;y--);
}
}
void lcdwrdata(unsigned char dat)
{
rs=1;//写数据时RS拉高
lcden=1;
P0=dat;
lcddelay(6);
lcden=0;
lcddelay(6);
}
void lcdwrcom(unsigned char cdat)//写指令数据到LCD
{
rs=0;//写指令时RS拉低
lcden=1;
P0=cdat;//
lcddelay(6);//不加此延时将导致不能写入指令,但能写入显示数据
lcden=0;
lcddelay(6);
}
void lcd_init()
{
lcdwrcom(0x38);//0x38设置显示模式为:16X2 显示,5X7 点阵,8 位数据接口
lcdwrcom(0x0c);//打开显示光标闪烁
lcdwrcom(0x06);//
lcdwrcom(0x01);//
}
/******************************************************************************/
void InitADC()
{
P1ASF = 0xf;
ADC_RES = 0;
ADC_CONTR = ADC_POWER|ADC_SPEEDL;
}
unsigned int GetADCResult(unsigned char ch)
{
unsigned int AD_result;
ADC_RES = 0;
ADC_RESL = 0;
ADC_CONTR = ADC_POWER | ADC_SPEEDL | ch | ADC_START;
_nop_(); //Must wait before inquiry
_nop_();
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG));//Wait complete flag
ADC_CONTR &= ~ADC_FLAG; //Close ADC
AD_result = ADC_RES;
AD_result <<= 2;
AD_result += ADC_RESL;
return AD_result; //Return ADC result 10bit
}
unsigned int GetResult(unsigned char ch)
{
unsigned int ADC_min, ADC_max, ADC_tmp, ADC_result, ADC;
unsigned char i, j;
ADC = 0;
for(j = 0; j < 16; j++)
{
ADC_result = 0;
ADC_min = ADC_max = GetADCResult(ch);
for(i = 0; i < 8; i++)
{
ADC_tmp = GetADCResult(ch);
if(ADC_tmp < ADC_min)
{
ADC_result += ADC_min;
ADC_min = ADC_tmp;
}
else if(ADC_tmp > ADC_max)
{
ADC_result += ADC_max;
ADC_max = ADC_tmp;
}
else
{
ADC_result += ADC_tmp;
}
}
ADC_result /= 8;
ADC += ADC_result;
}
ADC /= 16;
return ADC;
}
/******************************************************************************/
void TaskInit() _task_ 0
{
char n;
rw = 0;
lcddelay(200);
lcden = 0;
lcd_init();
InitADC();
os_wait2(K_TMO, 80);
TMOD |= 0x10;
os_create_task(0);
os_create_task(1);
os_create_task(2);
os_delete_task(0);
}
void TaskDisp() _task_ 1
{
float fv;
char buf[16], i, n;
while(1)
{
n = sprintf(buf, "U:%5.2fV I:%4.2fA", ADC_v, ADC_i);
lcdwrcom(0x00|0x80);
for(i=0;i<n;i++)
{
lcdwrdata(buf
); lcddelay(100); } for(;i<16;i++) { lcdwrdata(0x10); lcddelay(100); } os_wait2(K_TMO, 40); }} void TaskGetPowerInfo() _task_ 2{ unsigned int v_ref, v_out, v_i; while(1) { v_ref = GetResult(0); v_out = GetResult(1); v_i = GetResult(2); ADC_v = v_out * 2.50f / v_ref; ADC_i = v_i * 2.50f / v_ref; os_wait2(K_TMO, 50); }}
[ 此帖被senya21在2012-10-25 11:14重新编辑 ]