当前位置: 首页 > java >正文

《嵌入式硬件(三):串口通信》

UART通信基础概念

并行与串行

  • 并行:同一时刻可传输多个比特
  • 串行:同一时刻仅传输一个比特

通信模式

  • 全双工:收发可同时进行
  • 半双工:收发不可同时进行
  • 单工:单向通信

串口通信格式(TTL电平)

        串口通信格式,以TTL为例

  1. 空闲时数据线为高电平;
  2. 发送发发送一个低电平表示起始位;
  3. LSB低位,MSB高位(先发低位),发送的第一个比特是最低为(最右边);
  4. 校验位分为奇校验,偶校验和无校验。奇校验是指确保数据位加上校验位中"1",1的总数为奇数;偶校验是指确保数据位加上校验位中"1",1的总数为偶数;Odd奇校验,enev,偶校验
  5. 为保证下一个字节发送前的起始位能够表现出来,校验位之后发送一个停止位1。

参数表示

  • 示例:9600, n, 8, 1
    • 波特率:9600
    • 校验位:无(n),Odd奇校验,enev偶校验
    • 数据位:8
    • 停止位:1

异步与同步判断

  • 同步:存在时钟线(SCL)
  • 异步:无时钟线

长距离通信问题与解决方案

主机间通信时的电器物理问题

        主机间通信无论采用并行还是串行方式,都无法避免一个物理现象:导线内阻不为零造成的电压衰减。以之前讨论的TTL电平为例,主机之间的距离会造成高电平在接收端出现衰减现象和串扰(指不同信号之间相互干扰导致信号失真)影响。TTL(Transistor-Transistor Logic)通常指的就是芯片引脚产生的电压,这个电压值跟选择的芯片有关,在51单片机系统下是5v;在2440下是3.3v等等。5vTTL通信距离通常被限制在10~20米之间。

解决长距离通信问题

        为解决这个问题IEEE(Institute of Electrical and Electronics Engineers)颁布了RS232标准,其中规定了:

        逻辑高电平(逻辑1):在-3V到-15V之间

        逻辑低电平(逻辑0):在+3V到+15V之间

        收发主机间有三根线,分别是收、发和地,因此RS232是全双工的。理论上RS232能够传输20~30米。

RS485的优势

        同理RS485使用两根信号线(A和B)来传输数据,通过比较A和B之间的电压差来识别信息,电压范围分别为+7V到+12V和-7V到-12V。正电压表示高电平,负电压表示低电平。这种差分信号传输方式提高了抗干扰能力。RS485的传输距离可达1200米,适用于大范围的数据传输需求。由于采用的是压差,RS485在传输数据的某一时刻,两根线都要用到,所以它是半双工的。

串口寄存器与波特率计算

寄存器配置

  • SCON:串口控制寄存器
  • PCON:电源控制寄存器(波特率倍增)
  •  

波特率公式
2^8-2^smod * focs / 32 / bps / 12

代码示例:数据类型大小测量

#include <reg52.h>
#include "delay.h"
#include <stdio.h>
#include <string.h>void init_uart(void)
{unsigned char t;t = SCON;t &= ~(3 << 6);t |= (1 << 6);t |= (1 << 4);SCON = t;t = IE;t |= (1 << 7);t |= (1 << 3);IE = t;PCON |= (1 << 7);t = TMOD;t &= ~(3 << 4);t |= (2 << 4);t &= ~(3 << 6);TMOD = t;TH1 = 204;TL1 = 204;TCON = (1 << 6);
}void send_char(char ch)
{SBUF = ch;while(0 == (SCON & (1 << 1)));SCON &= ~(1 << 1);
}void send_buffer(const char *p, int len)
{while(len--){send_char(*p++);}
}int main(void)
{int x;char y;const char *s = "Hello World!";int n = 10, m = 20;xdata char buffer[32];xdata char buffer1[1];init_uart();while(1){x = sizeof(int);sprintf(buffer, "size int = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(char);sprintf(buffer, "size char = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(float);sprintf(buffer, "size float = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(short);sprintf(buffer, "size short = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(double);sprintf(buffer, "size double = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(s);sprintf(buffer, "size * = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(short);sprintf(buffer, "size short = %d\n", x);send_buffer(buffer, strlen(buffer));x = sizeof(long);sprintf(buffer, "size long = %d\n", x);send_buffer(buffer, strlen(buffer));x = y;sprintf(buffer1, "big or small = %d", x);send_buffer(buffer1, strlen(buffer1));delay(0x9FFF);	}return 0;
}

输出结果示例
size int = 2
size char = 1
size float = 4
size short = 2
size double = 4
size * = 3
size short = 2
big or small = 0(大端)

主从通信协议解析

数据帧格式

  • 示例:AA 01 01 04 D2 82 0D
    • 起始符:AA
    • 从机地址:01
    • 功能码:01
    • 数据长度/寄存器地址:04
    • CRC16校验:D2 82
    • 结束符:0D

代码实现(解析帧)

        

#include <reg52.h>
#include "delay.h"
#include <stdio.h>
#include <string.h>
#include "digiter.h"void init_uart(void)
{unsigned char t;t = SCON;t &= ~(3 << 6);t |= (1 << 6) | (1 << 4);SCON = t;PCON |= (1 << 7);IE |= (1 << 7) | (1 << 4);t = TMOD;t &= ~(3 << 4);t |= (2 << 4);t &= ~(3 << 6);TMOD = t;TH1 = 204;TL1 = 204;TCON |= (1 << 6);
}xdata char rcv_buffer[64] = {0};
int pos = 0;				  void uart_handler(void) interrupt 4
{if((SCON & (1 << 0)) != 0){rcv_buffer[pos++] = SBUF;SCON &= ~(1 << 0);}
}void send_char(char ch)
{SBUF = ch;while((SCON & (1 << 1)) == 0);SCON &= ~(1 << 1);
}void send_buffer(const char *p, int len)
{while(len--){send_char(*p++);}	
}int digiter_num = 0;unsigned char sumOfTheArray(unsigned char *p, int len)
{unsigned char sum = 0;int i;for(i = 0;i < len;++i){sum += p[i];}return sum;
}void parse(void)		   //0xAA 0000 0000 1010 1010     unsigned char 
{if((unsigned char)rcv_buffer[0] == 0xAA && (unsigned char)rcv_buffer[pos - 1] == 0x0D){if((unsigned char)rcv_buffer[1] == 0x01){if(sumOfTheArray(rcv_buffer, 5) == (unsigned char)rcv_buffer[5]){unsigned char order;order = rcv_buffer[2];switch(order){case 1:digiter_num = 1234;break;case 2:P2 = 0;	break;default:break;}}}}
}int main(void)
{init_uart();while(1){if(pos != 0){delay(0xFFFF);	//0x04 	0xD2parse();	pos = 0;memset(rcv_buffer, 0, sizeof(rcv_buffer));}show_number(digiter_num);}return 0;
}

关键点总结

  • 校验和计算需匹配帧内数据
  • 中断处理接收数据时需清标志位
http://www.xdnf.cn/news/20150.html

相关文章:

  • Jenkins 监控方案:Prometheus + Grafana 实践
  • Java 学习笔记(进阶篇2)
  • 《Cocos Creator的2D、3D渲染使用记录》
  • 使用自定义固定公网URL地址远程访问公司内网OA办公系统,本地无需公网IP和专线让外网访问
  • 【Python基础】 19 Rust 与 Python if 语句对比笔记
  • Unity学习----【进阶】Addressables(二)--加载资源与打包及更新
  • Github | MoneyPrinterTurbo:自动化视频内容生成系统
  • 医疗AI中GPU集群设计与交付实践
  • Windows蓝屏解决方案(扩展)
  • C++进阶——继承 (1)
  • Dify on DMS,快速构建开箱即用的客服对话数据质检服务
  • Cursor Pair Programming:在前端项目里用 AI 快速迭代 UI 组件
  • STM32使用HAL库驱动铁电存储FM25CL64
  • 用 Shields.io 定制 README 个性徽章
  • 嵌入式铁头山羊stm32-SAR型ADC模块介绍、采样时间、转换时间-Day24
  • Web与Nginx
  • MCP 和 Fuction Call 有什么不同
  • Python基础(①④内存管理机制)
  • 【Element Plus 表单组件样式统一 CSS 文字特效实现指南】
  • 啥是两化融合?
  • 算法模板(Java版)_哈希表
  • 手写Java泛型,彻底掌握它!
  • 结合prompt分析NodeRAG的build过程
  • MySQL事务的四大特性(ACID)
  • 代码随想录二刷之“贪心算法”~GO
  • HTML 基本结构
  • 一篇文章带你彻底搞懂 JVM 垃圾收集器
  • 大数据开发计划表(实际版)
  • Python入门教程之数学运算符
  • 基于单片机智能水龙头/智能洗漱台设计