STM32
Contents
UART硬件知识 串口参数 串口电平 数据传输方向 串口内部结构 UART编程 STM32F1串口框架 编程步骤 示例代码 初始化 发送数据 接收数据 数据回显

1 UART硬件知识

UART(Universal Asynchronous Receiver and Transmitter)异步发送和接收,设备间通过发送TX、接收RX、地线GND连接:

1.1 串口参数

  • 波特率:每秒传输的码元个数。串口通信中采用一个二进制位表示一个码元,因此波特率=比特率(bit/s)
  • 起始位:先发出一个逻辑”0”的信号,表示传输数据的开始。
  • 数据位:可以是5~8位逻辑”0”或”1”,如ASCII码(7位),扩展BCD码(8位),一般为小端传输。
  • 校验位:一帧数据(数据位+校验位)中“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
  • 停止位:一个字符数据的结束标志,一帧数据传输完成后进入逻辑"1"状态,状态持续时间可编程控制(0.5、1、1.5、2 * 波特率bit/s)。在STM32中的功能:
    • 1停止位:默认值。
    • 2停止位:正常的USART,单线和调制解调器模式支持。
    • 0.5停止位:使用智能卡方式接收数据时使用。
    • 1.5停止位:在智能卡模式下收发数据时使用。

1.2 串口电平

1.2.1 TLL/CMOS

传输‘A’时的波形:

  • x~5V:逻辑1
  • 0~yV:逻辑0

1.2.2 RS232

传输‘A’时的波形:

  • -12V~-3V:逻辑1
  • +3V~+12V:逻辑0

电平转换芯片(232<->TTL/COMS):MAX3232、SP3232

1.2.3 RS485

使用差分信号传输:

  • 逻辑1:+2V~+6V
  • 逻辑0:-6V~-2V

电平转换芯片(485<->TTL/COMS):MAX485

1.3 数据传输方向

1.4 串口内部结构

  • 发送数据:CPU控制内存将数据通过FIFO传给UART模块,UART通过send shift register将数据发送出去,发送过程中会产生相应事件标志,告诉CPU发送状态;
  • 接收数据:获取接收引脚电平,逐位放入receive shift register,再放入FIFO,写入内存。接收过程中也会产生相应事件标志,表面接收状态。

2 UART编程

2.1 STM32F1串口框架

2.2 编程步骤

2.2.1 使能UART/GPIO模块时钟

RCC寄存器基地址0x40021000

2.2.2 配置GPIO引脚模式

2.2.3 设置串口参数

设置数据格式

如:115200, 8n1表示波特率为115200, 8个数据位, 没有校验位,1个停止位。(起始位都为1)

  • USART_CR1:设置数据位、校验位,使能USART

  • USART_CR2:设置停止位

  • USART_CR3:不使用硬件控制流

设置波特率

USARTDIV = DIV_Mantissa + (DIV_Fraction / 16) (DIV_Mantissa整数部分,DIV_Fraction小数部分),由USART->BRR寄存器配置:

如USART1采用72M的PCLK2作为时钟,配置波特率为115200:

USARTDIV = 72000000/16/115200 = 39.0625
DIV_Mantissa = 39
DIV_Fraction = 0.0625 * 16 = 1
USART1->BRR = (DIV_Mantissa << 4) | DIV_Fraction 

HAL库通过以下宏来配置:

#define UART_DIV_SAMPLING16(_PCLK, _BAUD)           (((_PCLK)*25U)/(4U*(_BAUD)))
#define UART_DIV_Mantissa(_PCLK, _BAUD)        		(UART_DIV_SAMPLING16((_PCLK), (_BAUD))/100U)	// f_CK/(16*baud)) -- 25/400 = 1/16
#define UART_DIV_Fraction(_PCLK, _BAUD)  			((((UART_DIV_SAMPLING16((_PCLK), (_BAUD)) - (UART_DIV_Mantissa((_PCLK), (_BAUD)) * 100U)) * 16U) + 50U) / 100U)	// 小数部分四舍五入 * 16

2.2.4 根据状态寄存器读写数据

  • 状态寄存器

  • 数据寄存器

3 示例代码

3.1 初始化

#define UART_DIV_SAMPLING16(_PCLK, _BAUD)           (((_PCLK)*25U)/(4U*(_BAUD)))
#define UART_DIV_Mantissa(_PCLK, _BAUD)        		(UART_DIV_SAMPLING16((_PCLK), (_BAUD))/100U)	// f_CK/(16*baud)) -- 25/400 = 1/16
#define UART_DIV_Fraction(_PCLK, _BAUD)  			((((UART_DIV_SAMPLING16((_PCLK), (_BAUD)) - (UART_DIV_Mantissa((_PCLK), (_BAUD)) * 100U)) * 16U) + 50U) / 100U)
// RCC寄存器复位值为 0x0000XX83, 即默认开启HSI时钟(8M)
#define USART_CLK	8000000

void uart_init(USART_TypeDef* usart, uint32_t baud)
{
	volatile uint32_t* preg;
	//1 使能APB2总线下的GPIOA、USART2; 0x40021000为RCC寄存器, 0x18为RCC_APB2ENR寄存器偏移地址
	preg = (volatile uint32_t*)(0x40021000 + 0x18);
	*preg |= (1 << 2) | (1 << 14);
	
	//2 配置PA9、PA10, GPIOA_CRH = 0x40010800 + 0x04
	preg = (volatile uint32_t*)(0x40010800 + 0x04);
	// PA9(USART1_TX)
	*preg &= ~(0xf << 4);
	*preg |= (0x9 << 4); 		// 10: Alternate function output Push-pull 01: Output mode, max speed 10 MHz
	// PA10(USART1_RX)
	// 复位时的默认值: 01: Floating input (reset state) 00: Input mode (reset state)	

	//3 设置串口参数
	//3.1设置数据格式&使能串口 
	// 使能串口、使能发送、使能接收
	usart->CR1 = (1 << 13) | (1 << 3) | (1 << 2);
	// 数据格式: 1 Start bit, 8 Data bits, n Stop bit 、不校验
	usart->CR1 &= ~((1 << 12) | (1 << 10));
	// 1 Stop bit
	usart->CR2 &= ~(3 << 12);
	// 不使用硬件控制流 RTSE = CTSE = 0
	usart->CR3 &= ~((1 << 8) | (1 << 9));
	
	//3.2 波特率 baud = f_CK/(16*USARTDIV)), 则USARTDIV = f_CK/(16*baud))   --- f_CK(PCLK1 for USART2, 3, 4, 5 or PCLK2 for USART1)
	usart->BRR = (UART_DIV_Fraction(USART_CLK, baud) & 0x0f) | (UART_DIV_Mantissa(USART_CLK, baud) << 4);
}

3.2 发送数据

  • 发送一个字符
void putchar(USART_TypeDef* usart, char c)
{
	// Bit 7 TXE: Transmit data register empty  -- 1: Data is transferred to the shift register
	while (!(usart->SR & (1 << 7)));	// 发送数据寄存器内容没有全部转移到移位寄存器时等待
	usart->DR = c;
}
  • 发送字符串
void send_str(USART_TypeDef* usart, char* c)
{
	while(*c)
		putchar(usart, *c++);
}

3.3 接收数据

int getchar(USART_TypeDef* usart)
{
	// Bit 5 RXNE: Read data register not empty -- 1: Received data is ready to be read.
	while (!(usart->SR & (1 << 5)));	// 接收数据寄存器为空是等待, 即没有收到数据
	return usart->DR;
}

3.4 数据回显

int main()
{
	USART_TypeDef *usart1 = (USART_TypeDef*)0x40013800;
	while(1)
	{
		putchar(usart1, getchar(usart1));
	}
}

END

更多推荐

[010] [STM32] 串口通信基础知识与编程