六、数码管调试
紧接着上一篇,我们已经把下载部分都测试完成了,那么今天我们开始下一个部分的测试,首先我们先把硬件电路焊起来(由于一点点小失误,数码管只买了个共阳的,共阴的还在购物车里躺着,很尴尬,已经加急买下去了),最后数码管焊完之后成品如下(新焊接的部分如图红圈部分所示,包含原理图上的限流电阻和数码管部分,都是手册推荐的规格):
硬件部分焊完了,然后找个驱动代码测试一下,看看能不能点亮先。既然之前直接down的官网的原理图,那就再去把官网的那个配套的程序也下下来用着: 因为考虑到我们的板子和官方的板子引脚也用的差不多,那直接历程下载进去应该就能直接用,话不多说,直接冲,反正下载程序下错了既不会死机,也不会炸机,下就完了,下载完之后,数码管显示了这:
看到上面的视频,虽然显示有一点乱码,但是至少点亮了,另外相机拍摄会有一定频闪,这个原因简单的给大家说明下,这就是手机录制的帧率和数码管刷新的帧率不一致,这个问题百度很多 答案,实物上看得出前两位数码管一直在变,后两位一直稳如泰山的乱码,但是至少亮了,说明电路设计的和焊的应该不是问题。那我们开始打开程序分析。
- void main(void)
- {
- u8 i;
- u16 j;
-
- P_SW2 |= 0x80; //允许访问XSFR(扩展特殊功能寄存器)
-
- // XOSCCR = 0xc0; //启动外部晶振
- // while (!(XOSCCR & 1)); //等待时钟稳定
- // CLKDIV = 0x00; //时钟不分频
- // CKSEL = 0x01; //选择外部晶振
-
- P0M0 = 0x00;
- P0M1 = 0x00;
-
- P2n_push_pull(0xff); //COM0~COM7线设置为推挽输出
- P4n_push_pull(0xff); //SEG0~SEG7(P4.0~P4.7)线设置为推挽输出
- P3n_push_pull(0xf0); //SEG12~SEG15(P3.4~P3.7)线设置为推挽输出
-
- P2DR = 0x00; //COM0~COM7线设置为强电流驱动
- P4DR = 0x00; //SEG0~SEG7线设置为强电流驱动
- P3DR = 0x0f; //SEG12~SEG15线设置为强电流驱动
-
- COMEN = 0xff; //允许8个COM
- SEGENL = 0xff; //允许8个SEG 低字节
- SEGENH = 0xf0; //允许8个SEG 高字节
- LEDCTRL = 0x80 + (2<<4) + 0; //B7=1: ON, B5 B4: 0共阴, 1共阳, 2共阴共阳, 3保留, B2 B1 B0: 0占空比100%, 1~7占空比7/8~1/8
- LEDCKS = 8; //数码管COM扫描显示的主频时钟数 = 160*9*LEDCKS, 每个COM显示时间=1440*LEDCKS/fosc(MHz) us, COM扫描频率=fosc/(1440*LEDCKS *COM数).
- //例如 fosc = 11.0592MHz, 8个COM, LEDCKS = 8, 则每个COM显示时间=1440*8/11.0592 = 1042 us, COM扫描频率=11059200/(1440*8*8)=120Hz.
- //已知扫描时间t(us)计算 LEDCKS = t*fosc(MHz)/1440, 比如fosc = 24MHz, 要求1个COM扫描1ms, 则 LEDCKS = 1000*24/1440 = 16.7, 取整数17.
-
- for(i=0; i<8; i++)
- {
- LoadLED_A(i,i); //共阳,显示01234567
- LoadLED_B(i,i+8); //共阳,显示89ABCDEF
- }
- COM4_DAH_tmp = 0;
- COM5_DAH_tmp = 0;
- COM6_DAH_tmp = 0;
- COM7_DAH_tmp = 0;</font>
-
-
- P1n_pure_input(0xff); //Touch Key设置为高阻
- P5n_pure_input(0x0f);
- P0n_pure_input(0x0f);
-
- // TSCHEN = 0xffff; //TK0~TK15
- TSCHEN1 = 0xff; //TK0~TK7
- TSCHEN2 = 0xff; //TK8~TK15
- TSCFG1 = (7<<4) + 6; //开关电容工作频率 = fosc/(2*(TSCFG1[6:4]+1)), 放电时间(系统时钟周期数) 0(125) 1(250) 2(500) 3(1000) 4(2000) 5(2500) 6(5000) 7(7500) 最小3
- TSCFG2 = 1; //配置触摸按键控制器的内部参考电压(AVCC的分压比), 0(1/4) 1(1/2) 2(5/8) 3(3/4)
- // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, B7: TSGO, B6: SINGLE, B5: TSWAIT, B4: TSWUCS, B3: TSDCEN, B2: TSWUEN, B1 B0: TSSAMP
- // TSRT = 0x00; //没有LED分时扫描
- IE2 |= 0x80; //允许触摸按键中断
- EA = 1;
-
- delay_ms(50);
- // B_TK_Lowpass = 0; //禁止低通滤波
- B_TK_Lowpass = 1; //允许低通滤波
- for(read_cnt=0; read_cnt<40; read_cnt++) //读40次键, 将此值作为未触摸时的0点, 要求上电时不要触摸按键
- {
- // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, 4次平均, 读数大约为无平均的一半
- TSCTRL = (1<<7) + (1<<6) +1; //开始扫描, 2次平均, 读数大约为无平均的一半
- // TSCTRL = (1<<7) + (1<<6); //开始扫描, 只转换1次, 无平均
- B_ReadKeyOk = 0;
- for(i=0; i<100; i++)
- {
- if(B_ReadKeyOk) break;
- delay_ms(1);
- }
- }
- for(i=0; i<16; i++) TK_zero[i] = TK_cnt[i]; //保存0点
-
- B_TK_Lowpass = 1; //允许低通
- KeyState = 0;
- read_cnt = 0;</font>
-
- B_ReadKeyOk = 0;
- KeyValue = 10;
- KeyCode = 0;
- ShowKey();</font>
-
- while (1)
- {
- delay_ms(1);
-
- if(++TrigLimit >= 100) //触发转换
- {
- TrigLimit = 0;
- // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, 4次平均, 读数大约为无平均的一半
- TSCTRL = (1<<7) + (1<<6) +1; //开始扫描, 2次平均, 读数大约为无平均的一半
- // TSCTRL = (1<<7) + (1<<6); //开始扫描, 只转换1次, 无平均
- }
-
- if(B_ReadKeyOk) // 16个键都转换完毕
- {
- B_ReadKeyOk = 0;
- TrigLimit = 100;
- ShowValue(KeyValue); //显示读数
-
- j = KeyState; //读入上一次键状态
- for(i=0; i<16; i++)
- {
- if(TK_zero[i] > TK_cnt[i]) //计算与0点的差值
- {
- TK_zero[i]--; //缓慢0点跟随
- if((TK_zero[i] - TK_cnt[i]) >= T_KeyPress[i]/2) KeyState |= T_KeyState[i]; // 大于按键读数变量的1/2就是按下
- else if((TK_zero[i] - TK_cnt[i]) <= T_KeyPress[i]/3) KeyState &= ~T_KeyState[i]; // 小于按键读数变量的1/3就是释放
- }
- else
- {
- KeyState &= ~T_KeyState[i];
- if((TK_cnt[i] - TK_zero[i]) > 100) TK_zero[i] += 50; //差别很大, 则快速回0点
- else TK_zero[i] += 10; //差别不大, 则慢速回0点
- }
- }
- j = (j ^ KeyState) & KeyState; //检测键是否按下
- if(j != 0)
- {
- KeyCode = CheckKey(j); //计算键码 1~16
- i = (u8)j;
- COM4_DAH_tmp ^= (i << 4);
- COM5_DAH_tmp ^= (i & 0xf0);
- i = (u8)(j >> 8);
- COM6_DAH_tmp ^= (i << 4);
- COM7_DAH_tmp ^= (i & 0xf0);
-
- COM4_DAH = COM4_DAH_tmp;
- COM5_DAH = COM5_DAH_tmp;
- COM6_DAH = COM6_DAH_tmp;
- COM7_DAH = COM7_DAH_tmp;
-
- ShowKey();
- if(KeyCode == 15)
- {
- KeyValue--; //显示数值的键号-1
- KeyValue &= 0x0f;
- ShowValue(KeyValue); //显示读数
- }
- if(KeyCode == 16)
- {
- KeyValue++; //显示数值的键号+1
- KeyValue &= 0x0f;
- ShowValue(KeyValue); //显示读数
- }
- }
- }
- }
- }
复制代码
可以看到我们的程序部分如上,既然工程名称叫“16个触摸按键16个灯-8个共阴-8个共阳数码管显示”,那么推测程序就是一半灯驱动,一半触摸驱动,秉承着这个思路,我们开始分析程序,就看main开始的部分。
我们通过注释和函数的名称,变量名称等,我们把程序分为如上三块,红色的部分就是数码管(LED)驱动,绿色的部分就是触摸的驱动,最后while里面那个紫不紫,粉不粉(暂且叫他粉紫色,或者有谁知道这个具体叫啥的下边留言告诉我下)的就是数码管和触摸的组合的用户功能,那我们现在只测数码管,先把上面绿色和粉紫色的代码屏蔽掉,在下载进去看下效果如下:
这时候可以看到数码管显示贼稳了,说明这个代码基本可以用,给官方点个赞:handshake剩下的小细节我们就可以慢慢的通过测试对比然后修改了。
这时候打开STC8H的手册,找到LED的章节,刚开始是LED的描述,之前截图过了,这里就不赘述了,然后是LED驱动的结构图,
好吧,暂时还看不懂,那就先跳过(很多人会有一个误区,看手册喜欢精读每一句话,每一个字,直到看懂才接着往下看,个人觉得这个是有问题的.这就和英文考阅读理解一样,某个单词某个句子不理解,对我们理解全文来说影响不是特别大,我们完全可以不懂得先跳过,第一遍先看完全文,掌握大概,再回过头来精读句子来理解,看这个图也是一样,先把LED驱动的整个章节读完了,再来看这个图就会豁然开朗,原来是这个样子啊)。
然后下面就是接着寄存器描述,反正不多,我们一个一个的看:
- COMEN = 0xff; //允许8个COM
- SEGENL = 0xff; //允许8个SEG 低字节
- SEGENH = 0xf0; //允许8个SEG 高字节
复制代码
首先是手册的代码,然后是我们的例程里关于这个寄存器配置的代码,看到手册COMEN的寄存器里使能COM几就打开几通道,写1是打开,那我们的原理图里COM对应的8个IO都用上了,那这里和他一样就好,直接写FF。其次是SEGEN,这里我们原理图如下:实际上我们没有用完全部的SEG,只用了其中的八个,那这八个对应的是哪个通道呢,我们看一下手册,这部分寄存器描述这里没有详细展开,那我们去看引脚图(看下面第二张图,是不是一目了然了);
所以这里结合我们使用的通道,我们的初始化代码就可以改写为这样:
- COMEN = 0xff; //允许8个COM
- SEGENL = 0xf4; //允许P47-P44,P42作为SEG
- SEGENH = 0xE0; //允许P37-P35作为SEG
复制代码
好,再看下一个寄存器:
这个什么共阴共阳啊,打开LED驱动器,还有占空比(亮度)这些,我们是不是一模一样的,都不用去动他,那就接着下一个寄存器;
可以看到这个图片最后一行甚至都已经帮我们算好了这个建议值,我们这例程里写的是8,其实当然我们肉眼现在看起来不闪烁,那我也就不去动他了,实际上要按照推荐的这个75hz的刷新率的话,应该是 11059200/160/8/2/LEDCKS≥75HZ,因为我们用的共阴共阳模式,所以Ncom应该是16来计算,参考上图的蓝色小字。
然后接着下一个寄存器:
当然这个数据是直接显示的寄存器,也就是说共阴的数码管数据写入COM_DC就能直接显示了,那岂不是爽飞了,都不需要我们自己去刷新数码管,然后看了下后面的描述,为了方便大家看,我这里整理了一个表格,结合了我的板子去测试:
首先最上面一行DA_H和DA_L就是板子上的16个SEG引脚。坐标上的COM0-COM7就是COM引脚,因为com可以作为共阴和共阳所以,这边com分成了两组,但是引脚都是一样的,然后我的板子上P37接了数码管的d,也就是DA_H的最高位bit7接了d的引脚,所以我板子上焊的那个数码管我想点亮她显示一个1,就是数码管的b和c这两段要点亮,还是共阳的数码管。
综上所述,就是选择共阳的COM0的那一行,然后b和c的那一列,在他们交会的地方写1,最后下图所示
是不是就一目了然了,换成代码就是这么两行,就可以在数码管上显示1了; - COM0_DAH = 0X40;
- COM0_DAL = 0X04;
复制代码
当然其实仔细分析一下我们的代码,其实在代码里还有如下几行, - P2n_push_pull(0xff); //COM0~COM7线设置为推挽输出
- P4n_push_pull(0xff); //SEG0~SEG7(P4.0~P4.7)线设置为推挽输出
- P3n_push_pull(0xf0); //SEG12~SEG15(P3.4~P3.7)线设置为推挽输出
-
- P2DR = 0x00; //COM0~COM7线设置为强电流驱动
- P4DR = 0x00; //SEG0~SEG7线设置为强电流驱动
- P3DR = 0x0f; //SEG12~SEG15线设置为强电流驱动
复制代码
那么第一个 P2n_push_pull的函数跳转过去之后我们就可以看到他的源码:- #define P2n_push_pull(bitn) P2M1 &= ~(bitn), P2M0 |= (bitn)
复制代码
其实就是用了一个宏定义去将他设置为推挽输出的模式,用来增强驱动能力,说白了就是我们驱动数码管用到哪几个引脚,我们就把哪几个设置为推挽输出,像我这里就可以写成 - P2n_push_pull(0xff); //COM0~COM7线设置为推挽输出
- P4n_push_pull(0xf4); //SEG0~SEG7(P4.4~P4.7,P4.2)线设置为推挽输出
- P3n_push_pull(0xE0); //SEG12~SEG15(P3.5~P3.7)线设置为推挽输出
复制代码
最后的PxDR是为了增强电流驱动,可以看到下图我们需要大电流的端口写0就好。所以这里我们可以这么写 - P2DR = 0x00; //COM0~COM7线设置为强电流驱动
- P4DR = 0x0B; //SEG4~SEG7,SEG2线设置为强电流驱动
- P3DR = 0x1f; //SEG13~SEG15线设置为强电流驱动
复制代码
当然上面的PxDR 也好,P2n_push_pull也好,都是为了设置模式的,要是大家模式不会设置,直接用ISP软件在线配置也可以,办法参考如下,实在不会用的也可以问问自己的销售经理,或者下面留个言:
好了,那么上面的步骤全部结合起来,最终的代码就是这样: - void main(void)
- {
- u8 i;
- u16 j;
-
- P_SW2 |= 0x80; //允许访问XSFR(扩展特殊功能寄存器)
-
- // XOSCCR = 0xc0; //启动外部晶振
- // while (!(XOSCCR & 1)); //等待时钟稳定
- // CLKDIV = 0x00; //时钟不分频
- // CKSEL = 0x01; //选择外部晶振
-
- P0M0 = 0x00;
- P0M1 = 0x00;
-
- P2n_push_pull(0xff); //COM0~COM7线设置为推挽输出
- P4n_push_pull(0xf4); //SEG0~SEG7(P4.4~P4.7,P4.2)线设置为推挽输出
- P3n_push_pull(0xE0); //SEG12~SEG15(P3.5~P3.7)线设置为推挽输出
-
- P2DR = 0x00; //COM0~COM7线设置为强电流驱动
- P4DR = 0x0B; //SEG4~SEG7,SEG2线设置为强电流驱动
- P3DR = 0x1f; //SEG13~SEG15线设置为强电流驱动
-
- COMEN = 0xff; //允许8个COM
- SEGENL = 0xf4; //允许P47-P44,P42作为SEG
- SEGENH = 0xE0; //允许P37-P35作为SEG
-
- LEDCTRL = 0x80 + (2<<4) + 0; //B7=1: ON, B5 B4: 0共阴, 1共阳, 2共阴共阳, 3保留, B2 B1 B0: 0占空比100%, 1~7占空比7/8~1/8
- LEDCKS = 8; //数码管COM扫描显示的主频时钟数 = 160*9*LEDCKS, 每个COM显示时间=1440*LEDCKS/fosc(MHz) us, COM扫描频率=fosc/(1440*LEDCKS *COM数).
- //例如 fosc = 11.0592MHz, 8个COM, LEDCKS = 8, 则每个COM显示时间=1440*8/11.0592 = 1042 us, COM扫描频率=11059200/(1440*8*8)=120Hz.
- //已知扫描时间t(us)计算 LEDCKS = t*fosc(MHz)/1440, 比如fosc = 24MHz, 要求1个COM扫描1ms, 则 LEDCKS = 1000*24/1440 = 16.7, 取整数17.
-
-
- COM0_DAH = 0X40;
- COM0_DAL = 0X04;
-
- // P1n_pure_input(0xff); //Touch Key设置为高阻
- // P5n_pure_input(0x0f);
- // P0n_pure_input(0x0f);
-
- //// TSCHEN = 0xffff; //TK0~TK15
- // TSCHEN1 = 0xff; //TK0~TK7
- // TSCHEN2 = 0xff; //TK8~TK15
- // TSCFG1 = (7<<4) + 6; //开关电容工作频率 = fosc/(2*(TSCFG1[6:4]+1)), 放电时间(系统时钟周期数) 0(125) 1(250) 2(500) 3(1000) 4(2000) 5(2500) 6(5000) 7(7500) 最小3
- // TSCFG2 = 1; //配置触摸按键控制器的内部参考电压(AVCC的分压比), 0(1/4) 1(1/2) 2(5/8) 3(3/4)
- //// TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, B7: TSGO, B6: SINGLE, B5: TSWAIT, B4: TSWUCS, B3: TSDCEN, B2: TSWUEN, B1 B0: TSSAMP
- //// TSRT = 0x00; //没有LED分时扫描
- // IE2 |= 0x80; //允许触摸按键中断
- // EA = 1;
-
- // delay_ms(50);
- //// B_TK_Lowpass = 0; //禁止低通滤波
- // B_TK_Lowpass = 1; //允许低通滤波
- // for(read_cnt=0; read_cnt<40; read_cnt++) //读40次键, 将此值作为未触摸时的0点, 要求上电时不要触摸按键
- // {
- // // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, 4次平均, 读数大约为无平均的一半
- // TSCTRL = (1<<7) + (1<<6) +1; //开始扫描, 2次平均, 读数大约为无平均的一半
- // // TSCTRL = (1<<7) + (1<<6); //开始扫描, 只转换1次, 无平均
- // B_ReadKeyOk = 0;
- // for(i=0; i<100; i++)
- // {
- // if(B_ReadKeyOk) break;
- // delay_ms(1);
- // }
- // }
- // for(i=0; i<16; i++) TK_zero[i] = TK_cnt[i]; //保存0点
- //
- // B_TK_Lowpass = 1; //允许低通
- // KeyState = 0;
- // read_cnt = 0;
- //
- // B_ReadKeyOk = 0;
- // KeyValue = 10;
- // KeyCode = 0;
- // ShowKey();
-
- while (1)
- {
- // delay_ms(1);
- //
- // if(++TrigLimit >= 100) //触发转换
- // {
- // TrigLimit = 0;
- // // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, 4次平均, 读数大约为无平均的一半
- // TSCTRL = (1<<7) + (1<<6) +1; //开始扫描, 2次平均, 读数大约为无平均的一半
- // // TSCTRL = (1<<7) + (1<<6); //开始扫描, 只转换1次, 无平均
- // }
- //
- // if(B_ReadKeyOk) // 16个键都转换完毕
- // {
- // B_ReadKeyOk = 0;
- // TrigLimit = 100;
- // ShowValue(KeyValue); //显示读数
-
- // j = KeyState; //读入上一次键状态
- // for(i=0; i<16; i++)
- // {
- // if(TK_zero[i] > TK_cnt[i]) //计算与0点的差值
- // {
- // TK_zero[i]--; //缓慢0点跟随
- // if((TK_zero[i] - TK_cnt[i]) >= T_KeyPress[i]/2) KeyState |= T_KeyState[i]; // 大于按键读数变量的1/2就是按下
- // else if((TK_zero[i] - TK_cnt[i]) <= T_KeyPress[i]/3) KeyState &= ~T_KeyState[i]; // 小于按键读数变量的1/3就是释放
- // }
- // else
- // {
- // KeyState &= ~T_KeyState[i];
- // if((TK_cnt[i] - TK_zero[i]) > 100) TK_zero[i] += 50; //差别很大, 则快速回0点
- // else TK_zero[i] += 10; //差别不大, 则慢速回0点
- // }
- // }
- // j = (j ^ KeyState) & KeyState; //检测键是否按下
- // if(j != 0)
- // {
- // KeyCode = CheckKey(j); //计算键码 1~16
- // i = (u8)j;
- // COM4_DAH_tmp ^= (i << 4);
- // COM5_DAH_tmp ^= (i & 0xf0);
- // i = (u8)(j >> 8);
- // COM6_DAH_tmp ^= (i << 4);
- // COM7_DAH_tmp ^= (i & 0xf0);
- //
- // COM4_DAH = COM4_DAH_tmp;
- // COM5_DAH = COM5_DAH_tmp;
- // COM6_DAH = COM6_DAH_tmp;
- // COM7_DAH = COM7_DAH_tmp;
- //
- // ShowKey();
- // if(KeyCode == 15)
- // {
- // KeyValue--; //显示数值的键号-1
- // KeyValue &= 0x0f;
- // ShowValue(KeyValue); //显示读数
- // }
- // if(KeyCode == 16)
- // {
- // KeyValue++; //显示数值的键号+1
- // KeyValue &= 0x0f;
- // ShowValue(KeyValue); //显示读数
- // }
- // }
- // }
- }
- }
复制代码
上面,没用的都屏蔽掉了,然后按照上面分析留下我有用的,最终的实物效果如下:
这样数码管驱动也调通了哈哈~很有成就感!!! |