从串口通信到LED点阵屏(51单片机学习笔记) 完成矩阵键盘与密码锁的综合实践后这一阶段继续学习两个核心外设UART串口通信与8×8 LED点阵屏。串口让单片机具备了与电脑、外部模块双向通信的能力正式打开了嵌入式交互的大门LED点阵屏则在数码管动态扫描的基础上引入74HC595串转并芯片实现IO扩展完成图形与动画显示。两部分内容均沿用模块化编程思想进一步巩固了外设驱动的封装思路。一、串口通信1.1 串口基本概念串口是应用十分广泛的通用通信接口接线简单、协议成熟是单片机与外界交互的核心方式之一。51单片机内部集成了硬件UART通用异步收发器无需软件模拟时序即可实现稳定的双向通信。硬件连接最简单的双向串口通信只需三根线TXD发送端数据发送引脚RXD接收端数据接收引脚GND共地电平参考基准两个设备连接时TXD与RXD交叉相连GND互相连接仅需单向传输时也可以只接一根数据线。常见电平标准电平标准定义了逻辑0和逻辑1对应的电压范围不同标准不能直接互联TTL电平5V表示10V表示0单片机直接输出的就是TTL电平适合短距离板间通信。RS232电平-3~-15V表示13~15V表示0抗干扰能力更强适合长距离工业通信需要MAX232等电平转换芯片。RS485电平以两根线的压差表示信号2~6V为1-2~-6V为0差分传输抗干扰极强适合远距离多设备总线通信。通信基础概念全双工通信双方可以在同一时刻互相收发数据类似打电话。半双工通信双方可以收发但同一时间只能单向传输类似对讲机。单工只能一方发送、一方接收不可反向传输。异步通信双方没有共同时钟线各自约定波特率靠起始位、停止位同步数据。同步通信双方共用一根时钟线靠时钟节拍同步数据传输速度更快。总线挂载多个设备的公共数据线路所有设备共享同一组线缆通过地址区分。常用串行协议对比协议通信线通信方式特点UARTTXD、RXD全双工、异步点对点通信使用简单I2CSCL、SDA半双工、同步可挂载多个设备接线少SPISCLK、MOSI、MISO、CS全双工、同步速度快可挂载多个设备1-WireDQ半双工、异步单总线接线最少1.2 51单片机UART资源STC89C52内置1个UART外设共有4种工作模式模式0同步移位寄存器模式常用于扩展IO模式18位UART波特率可变最常用日常通信都使用该模式模式29位UART波特率固定模式39位UART波特率可变串口帧格式模式1一帧数据共10位1位起始位低电平 8位数据位低位在前 1位停止位高电平。空闲时总线保持高电平检测到下降沿即认为是起始位开始接收数据。SBUF数据缓存寄存器SBUF是串口的数据寄存器物理上是两个独立的8位寄存器但共用同一个地址执行写操作时写入的是发送寄存器数据会自动通过TXD引脚发出执行读操作时读取的是接收寄存器保存的是RXD引脚收到的数据1.3 波特率计算波特率是串口通信的速率单位是bps位每秒表示每秒传输的二进制位数。通信双方波特率必须一致否则会出现数据错乱。51单片机串口模式1的波特率由定时器1的溢出率决定公式如下波特率 (2^SMOD / 32) × (定时器1溢出率) 定时器1溢出率 晶振频率 / (12 × (256 - TH1))其中SMOD是PCON寄存器的最高位置1时波特率加倍。为什么常用11.0592MHz晶振因为11.0592MHz可以被4800、9600、19200等标准波特率精确1.4 串口编程实践基于上述理论知识我们可以编写51单片机的串口通信程序。以下代码示例展示了如何初始化串口波特率4800bps11.0592MHz并实现数据的发送与中断接收。1.4.1 初始化与发送函数UART.c首先我们创建一个UART.c文件包含串口初始化函数和字节发送函数。#include REGX52.H /** *brief 串口初始化4800波特率bps11.0592MHz *param 无 *retval 无 */ void UART_Init()//4800波特率bps11.0592MHz { SCON0x50; //这里打开了接收使能 PCON|0x80; TMOD0x0F; //把TMOD的高四位清零低四位保持不变 TMOD|0x20; //把TMOD的第6位置1其余位保持不变 TL10xF4; //设定定时初值 TH10xF4; //设定定时器重装值 ET10; //禁止定时器1中断 TR11; //启动定时器1不懂看定时器那一章 } /** *brief 串口发送一个字节数据 *param Byte 要发送的一个字节数据 *retval 无 */ void UART_SendByte(unsigned char Byte) { SBUFByte; while(TI0); //等待发送结束 TI0; //发送结束后TI会自动置1需要手动置0 }1.4.2 主函数main.c一个简单的测试主函数用于循环发送递增的数据。#include REGX52.H #include Delay.h #include UART.h unsigned char Sec; void main() { UART_Init(); while(1) { UART_SendByte(Sec); Sec; Delay(1000); } }1.4.3 启用中断的初始化与接收UART.c 修改版为了实现接收功能需要修改初始化函数开启总中断和串口中断。#include REGX52.H /** *brief 串口初始化4800波特率bps11.0592MHz *param 无 *retval 无 */ void UART_Init()//4800波特率bps11.0592MHz { SCON0x50; PCON|0x80; TMOD0x0F; //把TMOD的高四位清零低四位保持不变 TMOD|0x20; //把TMOD的第6位置1低四位保持不变 TL10xF4; //设定定时初值 TH10xF4; //设定定时器重装值 ET10; //禁止定时器1中断 TR11; //启动定时器1 EA1; //开启总中断 ES1; //开启串口中断 } /** *brief 串口发送一个字节数据 *param Byte 要发送的一个字节数据 *retval 无 */ void UART_SendByte(unsigned char Byte) { SBUFByte; while(TI0); //等待发送结束 TI0; //发送结束后TI会自动置1需要手动置0 }1.4.4 中断接收主函数main.c 修改版修改主函数并编写串口中断服务函数实现接收数据后回传并控制P2口LED。#include REGX52.H #include Delay.h #include UART.h void main() { UART_Init(); while(1) { } } void UART_Routine(void) interrupt 4 { if(RI1) { P2~SBUF; UART_SendByte(SBUF); RI0; //接收结束后RI会自动置1需要手动置0 } }代码说明初始化UART_Init函数配置了串口工作模式、波特率并开启了中断。发送UART_SendByte函数将数据写入SBUF发送并等待发送完成标志TI。接收中断服务函数UART_Routine在接收到数据RI1时触发将接收到的数据取反后输出到P2口控制LED同时通过串口将原数据回传“回显”。中断号串口中断的中断号为4。通过以上代码您便可以实现51单片机与电脑或其他设备之间的双向串口通信并能通过STC-ISP等软件进行数据收发测试与校验。1.5 串口编程实践HEX模式/十六进制模式/二进制模式以原始数据的形式显示文本模式/字符模式以原始数据编码后的形式显示以下是ASCII表二、LED点阵屏1. LED点阵屏介绍1.1 基本原理LED点阵屏由大量独立LED发光二极管按照矩阵形式排布组成通过控制每一颗灯珠的点亮与熄灭实现文字、图形、简单视频画面的显示。1.2 广泛应用公交报站屏户外广告屏信息公告牌单片机实验模块等场景1.3 分类方式按发光颜色单色只有一种颜色最常见红色只能显示该种颜色画面双色两种颜色可组合出第三种混合色全彩RGB三基色能够显示丰富彩色图像按像素规格点阵尺寸常见单元模块8×8、16×16点阵提示大型LED显示屏并不是单块大点阵是由许多小块点阵模块拼接组合而成。2. 显示原理LED点阵屏的结构类似于数码管只不过是数码管把每一列的像素以8字型排列而已LED点阵屏与数码管一样有共阴和共阳两种接法不同的接法对应的电路结构不同LED点阵屏需要进行逐行或逐列扫描才能使所有LED同时显示3. 开发板上的LED点阵屏A1到A8——阳极K1到K8——阴极4. C51的sfr、sbitsfr (special function register)特殊功能寄存器声明例sfr P0 0x80;声明P0口寄存器物理地址为0x80sbit (special bit)特殊位声明例sbit P0_1 0x81;或sbit p0_1 P0^1;声明P0寄存器的第1位可位寻址/不可位寻址在单片机系统中操作任意寄存器或者某一位的数据时必须给出其物理地址又因为一个寄存器里有8位所以位的数量是寄存器数量的8倍单片机无法对所有位进行编码故每8个寄存器中只有一个是可以位寻址的。对不可位寻址的寄存器若要只操作其中一位而不影响其它位时可用、|、^的方法进行位操作5. 使用sbit和宏定义新建变量控制单片机引脚sbit RCKP3^5; //RCLK sbit SCKP3^6; //SRCLK sbit SERP3^4; //SER #define MATRIX_LED_PORT P06. 常用于IO口扩展的74HC595单片机74HC595是串行输入并行输出的移位寄存器可用3根线输入串行数据8根线输出并行数据多片级联后可输出16位、24位、32位等常用于IO口扩展。7. LED点阵屏初始化SCK——用来送入一位2进制RCK——用来送入一组共八位2进制LED点阵屏初始化先将SCKRCK置0等待写入数据/** *brief LED点阵屏初始化 *param 无 *retval 无 **/ void MatrixLED_Init() { SCK0; RCK0; }8. 74HC595数据写入原理与函数实现利用74HC595的工作时序我们可以通过以下步骤写入数据将数据转换的二进制的一位写入SER引脚将SCK引脚置1上升沿输入数据将SCK置0继续等待SER写入下一位数据利用for循环重复以上步骤8次就写入了要输入的8位二进制数据注意QH最先写入QA最后写入for循环结束后将RCK先置1发送这组共8位二进制数据给QH到QA的引脚再置0等待下次写入以上做法可封装为函数/** * brief 74HC595写入一个字节 * param Byte 要写入的字节 * retval 无 */ void _74HC595_WriteByte(unsigned char Byte) { unsigned char i; for(i0;i8;i) { SERByte(0x80i); //赋的一位值遵循“非0既1” SCK1; SCK0; } RCK1; RCK0; }9. LED点阵屏显示一列数据利用第8点的74HC595写入函数将数据写入控制LED点阵屏的P0口我们可以写出让LED点阵屏显示一列数据的函数/** * brief LED点阵屏显示一列数据 * param Column 要选择的列范围0~7,0在最左边 * param Date 选择列显示的数据范围0x00~0xFF,高位在上1为亮0为灭 * retval 无 */ void MatrixLED_ShowColumn(unsigned char Column, unsigned char Date) { _74HC595_WriteByte(Date); MATRIX_LED_PORT~(0x80Column); Delay(1); MATRIX_LED_PORT0xFF; }※※※注意使用Delay(1);和MATRIX_LED_PORT0xFF;实现延时、位清零完成断选 → 位选 → 延时 → 位清零的流程让显示一列的数据不因快速扫描影响到下一列。硬件连接提示51开发板上的LED点阵旁的引脚GND和OE要相连LED点阵屏才能正常使用。10. 汇总的代码模块 MatrixLEDMatrixLED.c文件#include REGX52.H #include Delay.h sbit RCKP3^5; //RCLK sbit SCKP3^6; //SRCLK sbit SERP3^4; //SER #define MATRIX_LED_PORT P0 /** *brief 74HC595写入一个字节 *param Byte 要写入的字节 *retval 无 */ void _74HC595_WriteByte(unsigned char Byte) { unsigned char i; for(i0;i8;i) { SERByte(0x80i); //赋的一位值遵循“非0既1” SCK1; SCK0; } RCK1; RCK0; } /** *brief LED点阵屏初始化 *param 无 *retval 无 */ void MatrixLED_Init() { SCK0; RCK0; } /** *brief LED点阵屏显示一列数据 *param Column 要选择的列范围0~7,0在最左边 *param Date 选择列显示的数据范围0x00~0xFF,高位在上1为亮0为灭 *retval 无 */ void MatrixLED_ShowColumn(unsigned char Column, unsigned char Date) { _74HC595_WriteByte(Date); MATRIX_LED_PORT~(0x80Column); Delay(1); MATRIX_LED_PORT0xFF; }11. 绘制显示图案利用文字取模软件如D:\我早已麻痹\51单片机\普中A2开发板资料\5--开发工具\4-常用辅助开发软件\文字取模软件的工具记得修改选项将要显示的图案快速转化为要输入的16进制数据使用unsigned char的数组保存起来。12. 实例一LED点阵屏显示图形显示笑脸main.c文件#include REGX52.H #include Delay.h sbit RCKP3^5; //RCLK sbit SCKP3^6; //SRCLK sbit SERP3^4; //SER #define MATRIX_LED_PORT P0 /** *brief 74HC595写入一个字节 *param Byte 要写入的字节 *retval 无 */ void _74HC595_WriteByte(unsigned char Byte) { unsigned char i; for(i0;i8;i) { SERByte(0x80i); //赋的一位值遵循“非0既1” SCK1; SCK0; } RCK1; RCK0; } /** *brief LED点阵屏显示一列数据 *param Column 要选择的列范围0~7,0在最左边 *param Date 选择列显示的数据范围0x00~0xFF,高位在上1为亮0为灭 *retval 无 */ void MatrixLED_ShowColumn(unsigned char Column, unsigned char Date) { _74HC595_WriteByte(Date); MATRIX_LED_PORT~(0x80Column); Delay(1); MATRIX_LED_PORT0xFF; } void main() { SCK0; RCK0; while(1) { MatrixLED_ShowColumn(0,0x3C); MatrixLED_ShowColumn(1,0x42); MatrixLED_ShowColumn(2,0xA9); MatrixLED_ShowColumn(3,0x85); MatrixLED_ShowColumn(4,0x85); MatrixLED_ShowColumn(5,0xA9); MatrixLED_ShowColumn(6,0x42); MatrixLED_ShowColumn(7,0x3C); } }13. 实例二LED点阵屏显示动画显示滚动字幕动画显示的核心逻辑for(i0;i8;i) { MatrixLED_ShowColumn(i,Animation[iOffset]); } Count; if(Count15) { Count0; Offset; if(Offset89) { Offset0; } }使用Count计数起到延时的效果Count值超出15后归零并使位移量Offset增加图形整体向左移动一列。main.c文件#include REGX52.H #include Delay.h #include MatrixLED.h unsigned char code Animation[]{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x41,0x52,0xCC,0x74,0x42, 0x00,0x3A,0x01,0xFF,0x00,0x00,0x20,0x7F, 0x80,0x54,0xE9,0x5D,0x4B,0x6A,0xDD,0x49, 0x00,0x00,0x49,0x4A,0x50,0x62,0xF9,0x62, 0x51,0x4A,0x49,0x00,0x00,0x10,0x21,0x69, 0x29,0x29,0xFF,0x29,0x29,0x29,0x21,0x00, 0x00,0xFF,0x92,0x92,0x92,0x92,0x92,0x92, 0xFF,0x00,0x00,0x10,0x20,0xFF,0x10,0x00, 0x51,0x56,0xF8,0x54,0x72,0x11,0x00,0x00, 0x32,0x54,0x90,0x92,0xBF,0x10,0x14,0x12, 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00 }; //刘俊杰生日快乐字模数据 void main() { unsigned char i,Offset0,Count0; MatrixLED_Init(); while(1) { for(i0;i8;i) { MatrixLED_ShowColumn(i,Animation[iOffset]); } Count; if(Count15) { Count0; Offset; if(Offset89) { Offset0; } } } }