UART总结

总线协议 – UART

一、 UART简介

UART: Universal Asynchronous Receiver Transmitter(通用异步收发器),

通用的串行、异步通信总线,它有两条数据线,实现全双工发送和接收。

1.1 通信基础

串行、并行:

  • 并行通信:总线在传递数据的时候,可以一次性收发多个位的数据。并行虽然快,但是要考虑多个线的资源占用,以及多条线互相干扰的问题;
  • 串行通信:每一位数据逐次传递;(IIC\SPI都是串行通信

单工、双工:

  • 单工通信:只能是单向的,发送器给接收器发送数据,而不能反过来;
  • 双工通信:可以是A到B,也可以是B到A;
    • 半双工:不能同时发生;只有一根线,发送时就不能接收;
    • 全双工:可以同时发送和接收,有两根线;

波特率:

  • 波特率:描述UART通信速度,单位bps,(bit per second),每秒传送的bit的数量;

    • 每秒的波特数,波特等于单个信号包含的码元数量
  • 比特率:是指每秒传送的比特(bit)数。单位为 bps(Bit Per Second),比特率越高,每秒传送数据就越多。

  • 波特率:表示每秒钟传送码元符号的个数,是衡量数据传送速率的指标。

    • 在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。波特率是传输通道频宽的指标。
    • 比特率=波特率x单个调制状态对应的二进制位数
    • 两相调制(单个调制状态对应1个二进制位)的比特率等于波特率;四相调制(单个调制状态对应2个二进制位)的比特率为波特率的两倍;八相调制(单个调制状态对应3个二进制位)的比特率为波特率的三倍;依次类推。

1.2 UART帧格式

  • 起始位:1bit的0,标志发送开始;因为空闲时为1,所以开始为0;
  • 数据位: 5-8bit,先发低位,后发高位
  • 校验位:可有可无;奇偶校验,是校验奇数还是偶数个1;
  • 停止位:高电平1;
  • 空闲位:

注意: UART空闲的时候,用高电平表示;

串口协议从低位先发,最后发高位;

串口每次都只发一个byte,下一个byte又重新开始起始位,是因为它是异步的,发送方和接收方的时钟不是同一个,防止由于时钟不同步导致误差累积;

IIC和SPI都是同步通信,可以发多个字节;

1.3 Tx & Rx

串口的信号一般较弱,通信距离短,容易被干扰,所以增加了一块专有芯片用来增加抗干扰能力,使发送距离更远;(TTL信号转为232信号)

发送STEPs:

  1. 待发送数据放入FIFO
  2. 增加了UART协议的各种头,比如起始和校验,然后拷贝放入shifter;
  3. shifter每个bit依次移位到引脚来发送,先发低位,后发高位

即发送时: FIFO –> shifter –> TxD;

接收时顺序相反,RxD –> shifter –> FIFO;

1.4 UART工作模式

CPU会通过总线,和接收和发送的BUFFer相连,用来读写数据,另外,也和控制单元相连,对控制单元的设置进行修改;

look-back: 回环模式look-back是在内部将Tx和Rx短接,一般用于测试;

三种常见的工作模式:

  1. polling 轮询
    • CPU不断地对FIFO进行访问,查询需要的数据有没有发过来;
    • CPU的状态: 工作 –> 轮询 –> 工作 –> 轮询 …
    • 直到查找到需要的数据;
    • 费时费力,消耗资源
  2. interrupt 中断
    • CPU和FIFO约定,CPU不主动去查询,当FIFO收到数据要发送过来的时候,给CPU发送中断信号,CPU中断当前工作,接收FIFO的数据;
    • 比polling模式省资源
  3. DMA 直接存储器访问
    • CPU收到FIFO的数据,最终也是要放入某一块内存中去使用的;
    • DMA模式跳过CPU,当FIFO数据过来时,直接放入需要的内存中;

二、 polling模式实验(以qn9090为例)

2.1 主函数

usart_polling.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int main(void)
{
uint8_t ch;
usart_config_t config; // 配置波特率、帧格式、上升下降沿、奇偶校验、停止位等

/* Security code to allow debug access */
SYSCON->CODESECURITYPROT = 0x87654320;

/* attach clock for USART(debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

/* reset FLEXCOMM for USART */
RESET_PeripheralReset(kFC0_RST_SHIFT_RSTn);

BOARD_BootClockRUN();
BOARD_InitDebugConsole();
BOARD_InitPins();
/*
* config.baudRate_Bps = 115200U;
* config.parityMode = kUSART_ParityDisabled;
* config.stopBitCount = kUSART_OneStopBit;
* config.loopback = false;
* config.enableTx = false;
* config.enableRx = false;
*/
USART_GetDefaultConfig(&config); // 使用默认配置,传入config的引用
config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE; // 客制化usart配置
config.enableTx = true;
config.enableRx = true;

USART_Init(DEMO_USART, &config, DEMO_USART_CLK_FREQ); // 使用config和外部时钟初始化USART实例,其中外部时钟只在设置波特率时使用

USART_WriteBlocking(DEMO_USART, txbuff, sizeof(txbuff) - 1); // 将数组中的值写入

while (1)
{
PRINTF("READ:\n\r");
USART_ReadBlocking(DEMO_USART, &ch, 1); // 数据逐个从FIFORD读入ch,这里只读入一位
PRINTF("\n\rWRITE:\n\r");
USART_WriteBlocking(DEMO_USART, &ch, 1); // 数据逐个从ch写入FIFOWR,这里只读入一位,等待TX进入IDLE模式结束
PRINTF("\n\r");
}
}

2.2 只接收一个char

实验结果:

接收字符串

原始代码修改为接收一个定长5的array,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int main(void)
{
uint8_t array[6];
usart_config_t config;

/* Security code to allow debug access */
SYSCON->CODESECURITYPROT = 0x87654320;

/* attach clock for USART(debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

/* reset FLEXCOMM for USART */
RESET_PeripheralReset(kFC0_RST_SHIFT_RSTn);

BOARD_BootClockRUN();
BOARD_InitDebugConsole();
BOARD_InitPins();
/*
* config.baudRate_Bps = 115200U;
* config.parityMode = kUSART_ParityDisabled;
* config.stopBitCount = kUSART_OneStopBit;
* config.loopback = false;
* config.enableTx = false;
* config.enableRx = false;
*/
USART_GetDefaultConfig(&config);
config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;
config.enableTx = true;
config.enableRx = true;

USART_Init(DEMO_USART, &config, DEMO_USART_CLK_FREQ);

USART_WriteBlocking(DEMO_USART, txbuff, sizeof(txbuff) - 1);

while (1)
{
PRINTF("READ:\n\r");
USART_ReadBlocking(DEMO_USART, &array, sizeof(array) - 1);
PRINTF("\n\rWRITE:\n\r");
USART_WriteBlocking(DEMO_USART, &array, sizeof(array) - 1);
PRINTF("\n\r");
}
}

实验结果:

三、补充

3.1 SDK中典型使用

在NXP的QN9090的SDK中,提供了丰富的示例:

  • polling method 收发
  • interrupt method 收发
  • ringbuffer接收数据
  • DMA method 收发

文档 : MCUXpresso SDK API Reference Manual.pdf

3.2 串口的工作原理

一个byte的数据,如何转为串口输出的bit? 通过移位寄存器原理实现

移位寄存器原理:

初始状态: A3A2A1A0= 1011

然后Q3的输出,是在每个时钟节拍,按照这个先后顺序,把A3A2A1A0串行的输出出去。(小端)

其实利用的就是D触发器的特性。

如果我们使用8位的移位寄存器,就可以利用8个clk的时间,发一组8bit的数据通过一根导线传输出去。

作者

Gavin

发布于

2022-10-17

更新于

2022-10-17

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×