【嵌入式Linux_ARM裸机】---串口通信的实现

1. 通信的三个概念

通信的过程可以分为三个步骤:首先,发送方按照固定编码格式进行编码;其次,将编码后的信息发送到传输介质上;最后,接收方收到后进行解码得到有效信息。
同步和异步:发送方和接收方的时钟节拍一致叫同步,否则叫异步。如下雨所示,最上边的一根线就是同步信号线,发送端和接收端在相同的节拍下工作。
在这里插入图片描述
同步使用场景:当发送端和接收端频率固定时,使用同步信号。可以想象接收端一直在接收发送端的消息。
异步场景:在通信的双方频率不固定时,使用异步通信。

  • 电平信号和差分信号
    在这里插入图片描述
    电平信号:电平信号需要两根信号线,一根是参考线GND(0伏),另一根是信号线。电平信号值是信号线和参考电瓶线的差值。
    **差分信号:**没有参考线,有两根信号线,差分信号的信号值:两根信号线的电压差。
    **两种信号线特点:**电平信号容易受干扰,适合短距离传输。差分信号抗干扰能力强,适合长距离传输。
    电平信号的准备: RS232准备:-3~-15表示1, 3 ~15 表示0;TTL标准:+5表示1,0表示0.我们电脑后边的串口就是TTL标准的电平。
  • 并行通信和串行通信
    并行和串行主要说的是通信线的根数,就是发送方和接收方同时可以传递的信息量的多少。比如对电平信号来说,两个可以传送1位二进制,三根可传送两位2进制;传送8位需要9根。
    对差分信号来说,两根线可同时发送一位二进制,传送8位需要16根线。
    但是,实际上用的最多的还是串口通信,因为节省材料,并且现在串口用的最多的是传送调试信息。日常生活中网络通信,USB通信通过:异步,串行,差分方式。

2 串口通信的基本原理

  • 三根通信线:RX TX GND
    实际串口通信中,有9根线,但只有三根有效,就是RX TX GND。
  • 波特率,数据位,奇偶校验位,停止位
    这是SECURECRT链接串口输出调试信息的设置。
    在这里插入图片描述
    波特率:就是一秒内传输的二进制位数,常见的有9600,115200.双方的波特率不一致,收发就会出现问题。
    数据位:发送端是一个周期一个周期的,通常将发送信息编码成ACSII码,因此就是一个周期内发送8位。
    开始位:开始位设置为双方约定的高电平和低电平,当发送方收到二进制流时,就知道从哪里开始了。
    停止位:停止位是串口通信双方事先指定的,是由通信线上的电平变化来反映的。常见的停止位有1位,1.5位,2位。
    奇偶校验位:就是把数据位一一加起来,如果是偶数则奇偶校验位就是1,如果是奇数则奇偶校验位是0(看自己定义)。
  • 信息是以二进制流的方式在信道上传输
  • DB9接口介绍
    DB9是串口通信早期比较常用的一种规范。DB9中有9根线,3根为通信线,剩下的6根和流控(作用是控制发送流的速度)有关,现在一般都禁用流控,所以6根没有用。

3. 串口通信工作框图

在这里插入图片描述
从上图中可以看出,整个串口控制器包括Transmitter和receiver两部分,两部分功能彼此独立,transmitter负责向外发送信息,receiver负责从外部接受信息。
Transmitter由发送缓冲区和发送移位器组成。我们发送信息时,编码成二进制流,然后将一帧数据写入发送缓冲区(剩下的发送就是硬件自动完成的),单位了说明上图,具体来说明工作过程。然后,发送移位器会自动从发送缓冲区中读取一帧数据,通过移位一位一位发送在Tx通信线上。
接收缓冲区由Receiver 和 接收移位器构成。二进制流时通过Rx通信线进入我的接收移位器,然后接收移位器自动移位将该二进制位保存入接收缓冲区中,接收完一帧数据后产生一个中断给CPU,cpu收到中断后即可知道receiver收满了一帧数据,就会来处理这帧数据。
上面的过程,对于我们软件工程师来说,就是设置相应的寄存器,具体硬件细节根本不用我们管。设置寄存器时需要注意,要设置串口控制器的波特率发生器,就是要设置发送/接收的节拍时钟。波特率发生器其实就是个时钟分频器,它的工作需要源时钟(APB总线来),然后内部将源时钟分频(软件设置寄存器来配置),得到目标时钟,然后再用目标时钟产生波特率(硬件自动的)。

4. 具体实现

虽然可以直接设置寄存器的值,*(volatile unsigned int *) XXX地址值XX = 0xXXXX,但是为了代码清楚整洁,写成如下方式。
首先,定义寄存器;然后,初始化操作;最后,定义uart_putc函数发送(就是给寄存器赋值)。


#define  GPAOCON	0XE0200000  // 设置地址看数据手册,
#define  UCON0   	0XE2900004
#define  ULCON0  	0XE2900000
#define	 UMCON0  	0xE290000C
#define  UFCON0 	0xE2900008
#define  UBRDIV0 	0XE2900028
#define  UDIVSLOT0  0XE290002C

#define  UTRSTA0 	0XE2900010
#define  UTXH0 		0XE2900020
#define  URXH0 		0XE2900024
// 定义访问寄存器宏
#define  rGPA0CON  (*(volatile unsigned int *)GPAOCON)
#define  rUCON0		(*(volatile unsigned int *)UCON0)
#define  rULCON0	(*(volatile unsigned int *)ULCON0)
#define  rUMCON0 	(*(volatile unsigned int *)UMCON0)
#define  rUFCON0 	(*(volatile unsigned int *)UFCON0)
#define  rUBRDIV0	(*(volatile unsigned int *)UBRDIV0)
#define  rUDIVSLOT0  (*(volatile unsigned int *)UDIVSLOT0)

#define  rUTRSTA0 	(*(volatile unsigned int *)UTRSTA0)
#define  rUTXH0	(*(volatile unsigned int *)UTXH0)
#define  rURXH0  (*(volatile unsigned int *)URXH0)

// 串口初始化程序
void uart_init(void)
{
		// 初始化Tx Rx对应的GPIO引脚
	rGPA0CON &= ~(0xff<<0);			// 把寄存器的bit0~7全部清零
	rGPA0CON |= 0x00000022;			// 0b0010, Rx Tx
	
	// 几个关键寄存器的设置
	rULCON0 = 0x3;
	rUCON0 = 0x5;
	rUMCON0 = 0;
	rUFCON0 = 0;
	
	// 波特率设置	DIV_VAL = (PCLK / (bps x 16))-1
	// PCLK_PSYS用66MHz算		余数0.8
	//rUBRDIV0 = 34;	
	//rUDIVSLOT0 = 0xdfdd;
	
	// PCLK_PSYS用66.7MHz算		余数0.18
	// DIV_VAL = (66700000/(115200*16)-1) = 35.18
	rUBRDIV0 = 35;
	// (rUDIVSLOT中的1的个数)/16=上一步计算的余数=0.18
	// (rUDIVSLOT中的1的个数 = 16*0.18= 2.88 = 3
	rUDIVSLOT0 = 0x0888;		// 3个1,查官方推荐表得到这个数字
	
	
}

// 串口发送程序,发送一个字节
void uart_putc(char c)
{
	// 串口发送一个字节,其实就是把一个字节丢在发送缓冲区中
	// 因为串口控制器发送一个字节速度,远低于一个字节速度,所以CPU发送一个字节前,必须确认串口控制器
	// 当前缓冲区是空的(意思是串口已经发完了一个)
	while(!(rUTRSTA0 & (1 << 1)))
	rUTXH0 = c;

}

在这里插入图片描述
在这里插入图片描述
结果如下:(正在审核,先上图片)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值