资源共享一下,8*16点阵系列的程序,自己参考例程写的,不太完善还望多多包涵!
链接:https://pan.baidu.com/s/1EisrLCBLPE5Ksp7bytjSXA 提取码:xx35
对应此文章应该是文件名字为下图的文件夹!
硬件介绍
1. 主控: STC15F2K60S2
2. 点阵: 1588BR 8*8点阵 经过测试发现型号为 788BS的8*8的点阵引脚也如下,可以通用,只是大小不一样!
3. 放大电路:S8050 NPN三极管,5.1k电阻,470Ω电阻。
4. 时钟电路:DS1302 时钟模块
实物图片
1. 主控,STC-15最小系统板
2. DS1302时钟模块
3. 点阵显示及放大部分(之前阳极用的1k的电阻,驱动电流有点大,三极管也烫手,导致点阵烧了很多LED,后来换成5.1k电阻,三极管也不那么烫了,这个只是初步的,后边在做点阵的话,可以用74HC138,或者TM1640(这个芯片特别好用),然后现在就想从基础的开始,自己焊接一下电路板什么的。)
4. 整体的,时间显示
5. 整体的,日期显示
6. 整体的,星期显示
7. 整体的,年份显示
设计思路
- 程序简单电路图
大概的电路简图就是这个样子的,但是有一点需要特别注意
整个点阵模块的供电不要接在单片机最小系统上,需要另外接5V,或者3.3V单独供电,然后再与最小系统板共地。
如果点阵模块的供电接在最小系统,可能出现的问题是,单片机重新编译并下载程序后,可能程序还没开始运行,但是点阵已经通电了,出现的情况就是,点阵全灭,需要重新给点阵通电才可以,这个时候如果另外接点阵的供电电源即可解决问题!
点阵取模
行:第一块点阵和第二块点阵的阳极接一块,经过S8050,5.1K电阻接单片机P2口,最上边第一行是I/O口高位,取模时要注意!
列:两块点阵的阴极分别接S8050,5.1K电阻接到单片机,第一块接P0口,第二块接P1口,每块最左边均是I/O口最低位!
取模方式如下,不太懂的好好看看取模说明,另外,取模只跟硬件连接有关,一旦硬件确定,取模方式也随之固定,不会因为程序写法不一样而改变取模方式,之前我都把这点搞错了!
下边这个是我自己取的模,可以参考一下!想要什么图案,都可以自己用软件去画,或者直接用字符生成即可。
// 静态数组 数字库 3*8点阵取模
u8 code number_library[] = {
0x7F,0x41,0x7F,/*"0",0*/
0x21,0x7F,0x01,/*"1",0*/
0x4F,0x49,0x79,/*"2",0*/
0x49,0x49,0x7F,/*"3",0*/
0x78,0x08,0x7F,/*"4",0*/
0x79,0x49,0x4F,/*"5",0*/
0x7F,0x49,0x4F,/*"6",0*/
0x40,0x40,0x7F,/*"7",0*/
0x7F,0x49,0x7F,/*"8",0*/
0x79,0x49,0x7F,/*"9",0*/
};
// 静态数组 中间冒号字库 2*8点阵取模
u8 code sign_library[] = {
0x36,0x36, /* 冒号 */
0x08,0x08, /* -- */
};
// 静态数组 星期数组库 10*8点阵取模
u8 code week_library[] = {
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,/*"周一",0*/
0x04,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x04,/*"周二",0*/
0x02,0x22,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x22,0x02,/*"周三",0*/
0x7F,0x41,0x45,0x7D,0x41,0x41,0x7D,0x45,0x41,0x7F,/*"周四",0*/
0x00,0x21,0x29,0x29,0x3F,0x29,0x29,0x2F,0x21,0x00,/*"周五",0*/
0x11,0x12,0x14,0x18,0x70,0x70,0x18,0x14,0x12,0x11,/*"周六",0*/
0x00,0x00,0x7F,0x49,0x49,0x49,0x49,0x7F,0x00,0x00,/*"周日",0*/
};
程序
简单的对程序进行一个介绍,里边很多注释,应该可以看的懂。
- 程序变量,数组定义
disbuffer_sign :是一个标志位,当标志位为0/1的时候,改变动态数组 disbuffer1 里边的内容,为1/0,改变 disbuffer2。这样做的好处是,当前正用于显示的 数组 不会在显示的时候被改变数组内容。防止乱码出现,比如正在显示 disbuffer1 数组里边的内容,就改变 disbuffer2 数组里边的内容。
u8 *disbuffer :全局指针,用来代替数组 disbuffer1,disbuffer2里边的内容。
如下图,在显示结束后,不要忘记把标志位取反!
disbuffer1,disbuffer2 :就是两个动态数组,用来储存要显示的内容。
table :用来存放行刷新的数据,对应的P2口,由于是第一行是最高位,所以第一行显示就是0x80,一次类推!
- 延时函数
- 定时器0初始化函数
要用到定时器0中断,每隔5ms,或者1ms进中断刷新一次显示函数,这样就不用每次写的时候就要考虑加显示函数了!
- I/O口初始化函数
- 显示刷新函数
这个函数也是放在中断处理里边的 显示刷新函数。
u8 *disbuff :定义在显示函数内部的局部指针变量,只用在此函数内,避免与全局指针变量重复,作用一样!
column1,column2是两块点阵的列全部清0,相当于初始化。
k1到k16则是16列,每一列对应 disbuff 数组里边的 模值,disbuff[0] & table[hang],则应该是行刷新的,只有在选中此行的时候才显示对应行的码值,如果不雨一下的话,可能出现乱码,可以自己尝试一下,反正是代码调试嘛,不在乎多下载几次。如果不知道为啥取模取出来是这个样子的话,你可以把每一列单独看作一个8位的二进制,把取出来的十六进制数代入进去,在对应一下显示出来的内容,应该就可以理解为啥这个模是这个样子的。
row = table[hang] :则是显示行刷新,然后 hang++,然后如果 hang > 7 的话,初始化为0,因为只有8行,这个可以根据实际需要更改!
- DS1302 显示(时间,日期,星期,年份都类似)
/************************* DS1302时钟 时间显示 *************************************/
void Time_DS1302()
{
if(disbuffer_sign == 1)
disbuffer = disbuffer1;
else
disbuffer = disbuffer2;
// 时 十位 列 1 2 3
disbuffer[0] = number_library[(TIME[2]/16) * 3];
disbuffer[1] = number_library[(TIME[2]/16) * 3 + 1];
disbuffer[2] = number_library[(TIME[2]/16) * 3 + 2];
disbuffer[3] = 0x00;
// 时 个位 列 1 2 3
disbuffer[4] = number_library[(TIME[2]%16) * 3];
disbuffer[5] = number_library[(TIME[2]%16) * 3 + 1];
disbuffer[6] = number_library[(TIME[2]%16) * 3 + 2];
// 分 十位 列 1 2 3
disbuffer[9] = number_library[(TIME[1]/16) * 3];
disbuffer[10] = number_library[(TIME[1]/16) * 3 + 1];
disbuffer[11] = number_library[(TIME[1]/16) * 3 + 2];
disbuffer[12] = 0x00;
// 分 个位 列 1 2 3
disbuffer[13] = number_library[(TIME[1]%16) * 3];
disbuffer[14] = number_library[(TIME[1]%16) * 3 + 1];
disbuffer[15] = number_library[(TIME[1]%16) * 3 + 2];
// 冒号 列 1 2
if((TIME[0]%16) % 2)
{
if(disbuffer_sign == 1)
disbuffer = disbuffer1;
else
disbuffer = disbuffer2;
disbuffer[7] = sign_library[0];
disbuffer[8] = sign_library[1];
disbuffer_sign = ~disbuffer_sign;
delay(60000);
delay(60000);
}
else
{
if(disbuffer_sign == 1)
disbuffer = disbuffer1;
else
disbuffer = disbuffer2;
disbuffer[7] = 0x00;
disbuffer[8] = 0x00;
disbuffer_sign = ~disbuffer_sign;
delay(60000);
delay(60000);
}
// // 秒 测试
// disbuffer[9] = number_library[(TIME[0]/16) * 3];
// disbuffer[10] = number_library[(TIME[0]/16) * 3 + 1];
// disbuffer[11] = number_library[(TIME[0]/16) * 3 + 2];
//
// disbuffer[13] = number_library[(TIME[0]%16) * 3];
// disbuffer[14] = number_library[(TIME[0]%16) * 3 + 1];
// disbuffer[15] = number_library[(TIME[0]%16) * 3 + 2];
disbuffer_sign = ~disbuffer_sign;
}
一共16列,每一列都送进去对应得数,另外要注意,DS1302模块的数组,TIME[ ],里边是BCD码,转换成十进制数,十位要除以16,个位要求余16!然后比如初始化的秒是 0x23,则要注意这个是十六进制的数,加满16才进一,因此求余16得到的是最后一个3,对应数组显示的字符码应该是第9个到第11个,因此要 *3 ,然后依次加1加2。同理除以16得到的是第一个数2,对应的数组从6开始,到8结束!
中间第7 8 列,冒号跟随秒数的变化而闪烁,当秒的个位为奇数的时候,冒号亮,偶数的时候灭,在这个里边也要分别更改两个动态数组,标志位也要随之取反。
这个上边注释掉的是对秒显示的一个测试,可以不用写这么多,可以直接在要显示的对应位置上,把TIME[ ]数组取出来的数改到秒就好!
- 主函数
首先是对 I/O 口的初始化,定时器0的初始化,DS1302的初始化,接着在while循环里边,每次要先读取DS1302里边的时间,然后让时间显示30秒,日期显示10秒,星期显示10秒,年份显示10秒, 来回切换即可!
- 定时器0中断
P2 也就是行,是对点阵的一个消隐程序,接着就是只有一个显示函数,每次定时满多长时间,就进来执行一次显示函数!
附录总结
1. 程序代码还可以优化一下,另外之前试过列刷新,没有行刷新显示的快,因此还有很多写代码的方式可以改进。
2. 一定不要让点阵的电流过大,我这个点阵就是烧坏了很多LED,需要控制一下电流大小!
3. 切换方式有点单一,后续学习过后会继续改进!
4. 有错误的地方还望指点,有不懂的也可以评论问我哦!