切换到宽版
爱科技/爱创意/爱折腾;电子/数码爱好者的家!欢迎访问新版数码之家网站
  • 7647阅读
  • 30回复

[C51]东拼西凑制作电压电流表头 + 时钟(12C5A52S2方案) [复制链接]

上一主题 下一主题
离线widjrerpfnvd
 

发帖
1128
M币
2079
专家
9
粉丝
37
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2018-10-11
看见很多人用12C5A60S系列单片机做表头,正好手头有好几片拆机的单片机,
自己就琢磨做一个。期间借鉴参考了好多大神的方案和电路。
因为是第一次接触AD转换,所以比较费劲,发出来和大家一起学习。
(Mata8 的相机不好使了,图片质量很差,大家将就看看)
第一层楼:简单介绍,看看外观
//那个盒子,每个洞子、口子都是烙铁+什锦锉 弄的,费力费时
第二层楼:说说电路和部分代码  
//最有价值的部分就是代码,原文贴出来分享
第三层楼:看看电压表、电流表对比
//精度确实还是可以的,娱乐用一用没毛病



正常情况下,他是一个电子表
// 时钟的第一位前面和最后一位后面,我分别点了一个“点”,
//因为这个屏幕没有Dp,后面大家就看到用处了
//  这个电子表使用了12CR887+5,处理读取的乱码花了1天时间


顶部是一个移动充电宝的控制板  
//  价值7毛  ,3个18650并联供电,1.8A充电电流,可以给手机充电


左边是电源输入,右边是电源输出,还有一个自锁开关是切换时钟和表头显示用的
//   这个电源的出入接口非常不方便,插电只能使用小夹子



性价比非常高的一个充电宝板子  ,谁用谁知道  
//  话说,这个板子接上电池后需要外部充电一下,才能激活。每次拆下电池,都需要重新激活。


这个段式液晶屏,没有背光、没有接线定义,但是好在价格公道 3毛钱,HT1621 芯片,简单学习就会驱动
//    最困难的 在于一个一个找到   每位数字的段码表,用时2个晚上
//   这个屏幕,只需要电源正负和3条数据线就可以玩,所有还是非常方便的
//还有,这个外壳是 蛋糕房盛点心的盒子,0元。


本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共6条打赏M币+118专家+3
發騷友 专家 +2 以資鼓勵 2018-11-25
發騷友 M币 +100 以資鼓勵 2018-11-25
落花萧然 M币 +1 優秀文章 2018-11-23
q562379863 专家 +1 優秀文章 2018-10-12
topsjb M币 +8 // 电压 电流 测量很精准;厉害了我的哥; 2018-10-11
温力口 M币 +9 谢谢分享。。。。。 2018-10-11
离线widjrerpfnvd

发帖
1128
M币
2079
专家
9
粉丝
37
只看该作者 1楼 发表于: 2018-10-11
说说电路和代码(每行都有注释,看不懂可以留言)

参考这个电路。  其中采样电阻,我用的0.015欧(15毫欧),电压检测分压后入P1.7口,电流放大后入P1.0口
在单片机P1.0口,使用SPX1431作为基准(其实就是TL431 , 2.5伏基准),在运算中以基准为参考

        
这个是洞洞板布局图和具体接线图。 //我不会用电脑画图,所以大家将就一下。  
1.12C887使用了8条总线(P0口,无上拉电阻)和4条控制线(P2.4 - P2.7 。
2.LCD段码屏3条控制线(P2.0    2.2   2.3)。
3.自锁开关使用外部中断1,(P3.3引脚),选择低电平时触发,高电平时退出。
4.其他未使用的接口,全部用排针引出,方便扩展。
//////////////////////////////////////////////////////////////////////////////////////////


这个是段码屏  推算段码表  //  最原始,效率最低的办法

///////////////////////////////////////代码 介绍////////////////////////////////////////////
我是刚刚入门,做着玩,代码参考了好多现成的,所以大神勿笑,直接贴出来大家研究
也是因为入门,年龄大了,脑袋不好使,所以小问题也能研究半天,还找不到好办法克服
代码优化空间很大,大家拿去自己玩玩吧!   代码没有任何问题,只要注意相关接口一致就可以

#include <stc12c5a60s2.h>   //12C5A60 系列单片机,Ad功能使用必须包含这个文件
#include <intrins.h>
#include <math.h>
#include <stdio.h>
typedef unsigned char uchar;
typedef unsigned int  uint;
sbit HT1621_CS=P2^0;  //HT1621使能引脚
sbit HT1621_WR=P2^2;  //HT1621时钟引脚
sbit HT1621_DAT=P2^3; //HT1621数据引脚
sbit cs=P2^4;    //887 的控制脚
sbit as=P2^5;    //887 的控制脚
sbit rw=P2^6;    //887 的控制脚
sbit ds=P2^7;    //887 的控制脚
sbit beep=P4^4;    //蜂鸣器控制
sbit TV=P3^3;         //Time 和 V电压表 显示切换


#define BIAS 0x52 //0b1000 0101 0010 1/3bias 4com
#define SYSDIS 0X00 //0b1000 0000 0000 关系统振荡器和LCD偏压发生器
#define SYSEN 0X02 //0b1000 0000 0010 打开系统振荡器
#define LCDOFF 0X04 //0b1000 0000 0100 关LCD偏压
#define LCDON 0X06 //0b1000 0000 0110 打开LCD偏压
#define XTAL 0x38 //0b1000 0010 1000 外部接时钟  应该是0x38 使用外部振荡器
#define RC256 0X30 //0b1000 0011 0000 内部时钟
#define TONEON 0X12 //0b1000 0001 0010 打开声音输出
#define TONEOFF 0X10 //0b1000 0001 0000 关闭声音输出
#define WDTDIS 0X0A //0b1000 0000 1010 禁止看门狗#define FOSC 11059200L
#define BAUD 9600
#define ADC_POWER 0x80            //ADC power control bit
#define ADC_FLAG 0x10           //ADC complete flag
#define ADC_START 0x08           //ADC start control bit
#define ADC_SPEEDLL 0x00           //540 clocks
#define ADC_SPEEDL 0x20           //360 clocks
#define ADC_SPEEDH 0x40            //180 clocks
#define ADC_SPEEDHH 0x60            //90 clocks
#define _Nop() _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_()
uchar code Ht1621Tab[]={  };   //里面有19个0x00,用来清除屏幕内容
  
//////////////////////////  以下是心血,无资料段码屏的段码表///////////////////////////////////
uchar code S1[]={ //前3位的数字码表,地址分别为:0  2  4
0x5f,0x06,0x3d,0x2f,0x66,0x6b,   //0  1  2  3  4  5
0x7b,0x0e,0x7f,0x6f,            // 6  7  8  9
0x7e,0x73,0x59,0x37,0x79,0x78, //10-A  11-b   12-C   13-d   14-E   15-F
0x57      // 16-U
};
uchar code S2[]={  //后四位的数字码表,地址分别为:11  13  15  17
0x9f,0x06,0x3d,0x2f,0xa6,0xab,  //0  1  2  3  4  5
0xbb,0x0e,0xbf,0xaf,            // 6  7  8  9
0xbe,0xb3,0x99,0x37,0xb9,0xb8, //10-A  11-b   12-C   13-d   14-E   15-F
0x97      // 16-U
};
uchar code S_7[]={  // 米 字位  地址分别为:7
0x51,0x00,0x31,0x21,0x60,0x61,  //0  1  2  3  4  5
0x71,0x00,0x71,0x61,            // 6  7  8  9
0x20,0x24,0x2e,0x04              //0-减号 1-加号 2-米  3-中间一竖
};
uchar code S_9[]={  // 米 字位  地址为:9
0x0e,0x06,0x4c,0x4e,0x46,0x4a,  //0  1  2  3  4  5
0x4a,0x0e,0x4e,0x4e,            // 6  7  8  9
0x40,0x60,0xf0,0x20              //10-减号 11-加号 12-米  13-中间一竖
};


/////////////////////////////几个函数定义 ///////////////////////////////////////////////////


void DelayMs(uint iMs)        //延时 1Ms   ,不同于89C52系列单片机
{
uint i,j;
for(i=0;i<iMs;i++)
for(j=0;j<950;j++);
}void write_ds(uchar add,uchar da_ta) //写一个字节到DS12C887
{
cs=0; as=1; ds=1; rw=1;        
P0=add;                
as=0; rw=0;        
P0=da_ta;      
rw=1; as=1; cs=1;        
}
uchar read_ds(uchar add) //从DS12C887读取一个字节
{
uchar read_data;
as=1; ds=1; rw=1; cs=0;                    
P0=add;      
as=0; ds=0;          
P0=0xff;      
read_data=P0;    
ds=1; as=1; cs=1;              
return read_data;
}
/******************************************************
写数据函数,cnt为传送数据位数,数据传送为低位在前
*******************************************************/
void Ht1621Wr_Data(uchar Data,uchar cnt){
uchar i;
for (i=0;i<cnt;i++)
      {
       HT1621_WR=0;
       _Nop();
       HT1621_DAT=Data&0x80;   //1000 0000
       _Nop();
       HT1621_WR=1;
       _Nop();
      Data<<=1;
     }}
/********************************************************
命令写入函数         Cmd为写入命令数据            写入命令标识位100
********************************************************/
void Ht1621WrCmd(uchar Cmd){
HT1621_CS=0;
_Nop();
Ht1621Wr_Data(0x80,4); //写入命令标志100
Ht1621Wr_Data(Cmd,8); //写入命令数据
HT1621_CS=1;
_Nop();
}

/********************************************************
连续写入函数         Addr为写入初始地址,*p为连续写入数据指针, cnt为写入数据总数  HT1621的数据位4位,此处每次数据为8位,写入数据           总数按8位计算 ,实际上只有 低4位有效
********************************************************/
void Ht1621WrAllData(uchar Addr,uchar *p,uchar cnt) {  
uchar i;  
HT1621_CS=0;
Ht1621Wr_Data(0xa0,3); //写入数据标志101
Ht1621Wr_Data(Addr<<2,6); //写入地址数据
for (i=0;i<cnt;i++)
      {
        Ht1621Wr_Data(*p,8); //写入8位数据,后4位有效
        p++;
       }
HT1621_CS=1;
_Nop();
}
/*  输出单个数字   Addr代表数字所在位置   *p是指针变量,从2个字节操作*/
void Shuzi(uchar Addr,uchar p) {  
   HT1621_CS=0;
Ht1621Wr_Data(0xa0,3); //写入数据标志101
Ht1621Wr_Data(Addr<<2,6); //写入地址数据
Ht1621Wr_Data(p,8); //写入8位数据,8位有效    
HT1621_CS=1;
_Nop();}    
void Ht1621_Init(void) {        // HT1621初始化
HT1621_CS=1;
HT1621_WR=1;
HT1621_DAT=1;
DelayMs(20); //延时使LCD工作电压稳定
Ht1621WrCmd(BIAS);
Ht1621WrCmd(RC256); //使用内部振荡器
//Ht1621WrCmd(XTAL);  //使用外部振荡器
Ht1621WrCmd(SYSDIS);
Ht1621WrCmd(WDTDIS);
Ht1621WrCmd(SYSEN);
Ht1621WrCmd(LCDON);
}
////////////////////显示字符 临时储存
uchar Result_V[4]={0x00,0x00,0x00,0x00};
uchar Result_I[3]={0x00,0x00,0x00};
static void GetVoltage();
void InitADC()        // adc初始化功能函数
{
   P1ASF = 0x83;                   //1000,0011, 将 P1.0 P1.1 1.7置成模拟口
   ADC_RES = 0;               //Clear previous result
   ADC_CONTR = ADC_POWER | ADC_SPEEDLL;
   DelayMs(20);               //ADC power-on and delay
}
/*************************************************************************
功能:获取adc结果的函数
*************************************************************************/
unsigned long GetADCResult(uchar ch)
{
unsigned int ResultHigh,Result;
   ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
   _nop_();                        //Must wait before inquiry
   _nop_();
   _nop_();
   _nop_(); _nop_();
   while (!(ADC_CONTR & ADC_FLAG));//Wait complete flag
   ADC_CONTR &= ~ADC_FLAG;         //Close ADC
ResultHigh=ADC_RES;      //save the high 8byte data
Result=(ResultHigh<<2)|ADC_RESL;   //get 10byte ADC data
   return Result;                 //Return ADC result
}
static void GetVoltage()   //获取电压值的函数      100次平均后显示
{ uint i;
unsigned long Voltage,Current;
unsigned long V_0,V,I; //定义电压返回临时值和电压值

  for(i=1;i<=100;i++)  
  {
  V_0 += GetADCResult(0);            // 100次结果累加
    I += GetADCResult(1);       // 100次结果累加
    V += GetADCResult(7);       // 100次结果累加
  if(i==100)    // 够了100次,求平均值
  {  
        V_0 = V_0/100;  //通过通道0和当前通道的结果计算出实际电压
   //获取通道0的测量结果,将通道0结果当作一个标准的2.48v参考电压
     V = V/100;   //100次结果累加求平均
  I = I/100;   //100次结果累加求平均
  Voltage=(V*2480.0/V_0+15.60)/1.0104;
  //电压30V以内误差 正负15毫伏,标准数=(ADC数 + 15.60) /1.0104// 就这几个常数,是通过分散点记录、和初中方程式公式导出的 ,从效果来看,除了不能归零外,误差还是可以的
  Current=(I*24800.0/V_0-122.0)/0.773;    
  //计算后参数:0.773 * 标准数 +122= AD数值
  Result_V[3]=Voltage%10;
  Result_V[2]=Voltage%100/10;
  Result_V[1]=Voltage%1000/100;
  Result_V[0]=Voltage/1000;
  Result_I[2]=Current%100/10;   //0.0X 位
  Result_I[1]=Current%1000/100; //0.X0 位
  Result_I[0]=Current/1000;   //X.00 位
  DelayMs(200); }         // 延时一下,这样显示不会乱跳
  }
} //程序结束
void xianshi()  
{   uchar i,j,t;
      GetVoltage();
   for(i=0;i<=2;i++) //通过循环,动态显示 电流数据(前三位)
   {
    j=i*2;   t=Result_I;   Shuzi(j,S1[t]);  
   }
   for(i=0;i<=3;i++) //通过循环,动态显示 电压数据(最后四位)
   {
    j=i*2+11;   t=Result_V;   Shuzi(j,S2[t]);  
   }
   DelayMs(10);
}
void DIS_time()
{     uchar X1,X2,X3,X4;  
        uchar shi=0,fen=0,miao=0;
  shi=read_ds(4);
  fen=read_ds(2);
// miao=read_ds(0);
  X1=shi/10;   X2=shi%10;    
  X3=fen/10;   X4=fen%10;
  if(S1[X1]!=0xbf )     // 这里加入If  目的是过滤掉乱码。  可能好多人遇到过,读取过程中,有时候会读回//没用的代码,研究了半天,确定小时的十位数字 采集到0xbf 时就是乱码,需要屏蔽
//而且,之前的 米字位 闪烁,是在IF语句里面的,结果只要遇到乱码就不动了,//研究了2个晚上,才想到把它放在外面,里面遇见乱码就跳过,速度快不影响正常显示
//话说,这个887 的读取乱码,太恶心人了,暂时也只想到这个办法
  {
      Shuzi(2,S1[X1]);
      Shuzi(4,S1[X2]);
      Shuzi(11,S2[X3]);
      Shuzi(13,S2[X4]);  }
         Shuzi(7,S_7[10]);Shuzi(9,S_9[10]);  DelayMs(700); //米字位减号亮
         Shuzi(7,0x00);Shuzi(9,0x00);  DelayMs(600);      //米字位减号灭
    
}
void main()
{
P4SW=0x70;   //这条语句可以控制P4.4  P4.5   P4.6  为普通I/O口
InitADC();
Ht1621_Init(); //上电初始化LCD
DelayMs(1); //延时一段时间
//beep=0; DelayMs(600);   beep=1;  //蜂鸣器响一声,太吵,屏蔽了
  write_ds(0x0b,0x86);//0x84 12小时模式  0x86) 24小时模式    允许对ds12c887写
  write_ds(0x0A,0x20);//打开晶振,开始计时,SQW不使用  
       //write_ds(4,17); //第4位,控制小时  
     //write_ds(2,11); //第2位,控制分钟          
        //write_ds(0,0);    //第0位,控制秒
   write_ds(0x0B,0x06); //禁止对ds12c887写 ,24小时模式
  IT1=0; //外部中断1 为低电平触发
  EX1=1; //使能外部中断1
  EA=1;  //打开总中断
  TV=1; //按键触发中断
  Ht1621WrAllData(0,Ht1621Tab,19);   //清除1621寄存器数据,暨清屏
while(1){  
  Shuzi(0,0x00);Shuzi(15,0x00);Shuzi(17,0x00); //清除ADC显示时使用的第1位和最后两位的数据
   DIS_time();    //显示时间函数
  } //while
}  //main
void EX1_m() interrupt 2
{  Shuzi(7,0x00);Shuzi(9,0x00); //清除时间显示时使用的米字位数据
   xianshi();   //显示转换结果,具体转换速度,我没有算
}


[ 此帖被widjrerpfnvd在2018-10-11 23:20重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共2条打赏M币+35
chw321401 M币 +15 生命在于折腾 ...... 2018-10-13
q562379863 M币 +20 優秀文章 2018-10-12
离线widjrerpfnvd

发帖
1128
M币
2079
专家
9
粉丝
37
只看该作者 2楼 发表于: 2018-10-11
电压和电流对比
用自制表头和胜利VC9805A+  进行对比
1.可调电源最低电压为0.1伏,最高电压为47.8伏
2.表头前三位显示电流,后四位显示电压,都是精确到小数点后两位
//  其实感觉精确到小数点后一位就完全够用了
3、表头由于使用了LM358放大电路,技术有限,无法完全归零
//通过修改代码归零,废了1天晚上研究,不成功,放弃了
4、电流测试,使用可调电源短路电流,懒得搬出来我那“”笨重“”的电子负载了
// 已经测试过,效果和短路电流一样
5、总体来看,在测量1.00至最高数值期间,两个表头的误差在+-0.04左右,线性度“调教”的很好。
(电压表头在30伏时误差最大为0.04V , 电流表头在0.47安时误差最大为0.02A)
//  当然,可能胜利万用表也有误差的。  
//等我的 初始误差0.02% 的基准电压(美信Max6325 系列)  做好了,就可以给他们测试了


5V对比


10伏对比


20伏对比


30伏对比


40伏对比


最高电压47.8伏对比
//////////////////////////以下为 电源  短路电流对比////////////////////////////////////////

因为可调电源电压无法调零,所以最低电流为0.47安



1安电流对比


2安电流对比


3安电流对比


4安电流对比



表头最大显示电流4.6安

[ 此帖被widjrerpfnvd在2018-10-11 22:26重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共5条打赏M币+72
xiaomage M币 +25 谢谢分享,学习了! 2018-10-14
luly8818 M币 +1 優秀文章 2018-10-13
数码家园 M币 +13 - 2018-10-13
q562379863 M币 +20 優秀文章 2018-10-12
2545889167 M币 +13 不错,以資鼓勵 2018-10-11
离线yyglxc

发帖
4659
M币
1528
专家
1
粉丝
38
只看该作者 3楼 发表于: 2018-10-11
制作过程呢?
离线2545889167

发帖
13268
M币
21254
专家
302
粉丝
4824
只看该作者 4楼 发表于: 2018-10-11
不错,以資鼓勵
离线levifly

发帖
78
M币
202
专家
2
粉丝
3
只看该作者 5楼 发表于: 2018-10-11
    没有详细说明你的制作方案啊,期待继续~
离线温力口

发帖
60316
M币
84433
专家
569
粉丝
902
只看该作者 6楼 发表于: 2018-10-11
谢谢分享。。。。。
离线widjrerpfnvd

发帖
1128
M币
2079
专家
9
粉丝
37
只看该作者 7楼 发表于: 2018-10-11
回 levifly 的帖子
levifly:    没有详细说明你的制作方案啊,期待继续~ (2018-10-11 22:54) 回 levifly 的帖子

主要在2楼,已经更新了
离线topsjb

发帖
340
M币
1274
专家
2
粉丝
12
只看该作者 8楼 发表于: 2018-10-11
// 电压 电流 测量很精准;厉害了我的哥;
在线q562379863

发帖
4364
M币
8275
专家
37
粉丝
213
只看该作者 9楼 发表于: 2018-10-12
好帖子,马克,明日再看
快速回复
限80 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个