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

发布于:2025-09-06 ⋅ 阅读:(18) ⋅ 点赞:(0)

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 	0xD2
			 parse();	
			 pos = 0;
			 memset(rcv_buffer, 0, sizeof(rcv_buffer));
		}
		show_number(digiter_num);
	}

	return 0;
}

关键点总结

  • 校验和计算需匹配帧内数据
  • 中断处理接收数据时需清标志位

网站公告

今日签到

点亮在社区的每一天
去签到