参考这篇: STM32串口通信详解
1. 关于USART
USART ( universal synchronous / asynchronous receiver /transmitter) 是一种串行通讯协议 , 允许设备通过串行端口进行数据传输, USART 能够以同步或者异步的方式进行工作,在实际的运用中,我们主要使用的是它的异步通信模式
1.1 USART 工作模式 (异步)
- 通讯方式: 异步 ,无需共享或者同步时钟信号
- 数据格式: 发送和接受数据时, 数据被封装在帧中,通常包含起始位 数据位 可选的奇偶校验位 以及停止位
- 波特率: 使用先双方需要设置相同的波特率
- 流控制: 可选择硬件流控制如RTS/CTS)或者软件流控制 (如XON/XOFF)
- 连接方式: 点对点,通常用于两个设备之间
优点: 在于简单些和点对点的通讯效率
缺点: 没有外部的时钟进行同步, 通讯双方的时钟必须精确的匹配
1.2 USART 工作模式 (同步)
在同步模式下 , USART需要使用一个外部的时钟信号来同步数据的发送和接受
- 同步起始位: 同步模式使用特定的同步字符或位模式来标记数据帧的开始
- 数据位: 数据以固定的数据发送, 速率由外部时钟决定
- 奇偶校检位(可选) : 于异步模式相同, 用于错误检测
- 停止位: 在某些同步的USART中,停止位可能不被使用,因为时钟信号已经提供了数据帧 的同步
优点:优点是速度,因为外部时钟信号允许更快的数据率和更高的数据吞吐量 , 此外 由于时钟信号的存在,接受器能够更加准确的确定何时读取数据位
缺点 : 需要额外的时钟线
1.2 USART 和 UART 的差异
- 同步模式: USART 可以工作在同步模式下,但是需要额外的时钟信号来同步数据的发送和接受, 但是UART 不具备同步模式
- 功能: USART 通常提供更多的特性和配置选项,如数据位的长度,奇偶校检,多种停止位等
- 速度和效率: 在同步模式下, USART可以提供比异步模式(UART)更快的数据传输速度
- 硬件复杂性: USART的硬件实现比UART 复杂, 因为它需要处理同步和异步两种通讯方式
1.4 工作的框图
2. 流程
2.1 初始化GPIO : GPIO_init(void)
- 使能GPIO的时钟
- 设置GPIO引脚9 和 10 为复用功能 ,方便用作 USART1 的 TX(接受) 和RX(发送) 引脚
- 初始化GPIO 引脚设置 模式(复用) ,输出类型(推挽),上拉下拉(上拉)
2.2 USART的初始化 : USART_init(void)
- 使能 USART1 的时钟
- 配置USART1的参数 :波特率(115200) , 字节长度(8),硬件流控制(无),工作模式(发送),校检位(无), 停止位(1位)
- 使能USART1 使其工作
2.3 fputc函数重写
- 重写fputc函数以便printf可以使用USART发送数据。
将字符ch发送到USART1。 - 使用轮询方式等待发送完成(通过检查USART1的传输完成标志)。
- 返回写入的字符。
3. 代码
3.1 USART.h
#ifndef USART_H
#define USART_H
#include "stm32f4xx.h"
#include "stdio.h"
#include "stm32f4xx_usart.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
void GPIO_init(void);
void USART_init(void);
int fputc(int ch , FILE*f) ;
#endif
3.2 USART.c
#include "stm32f4xx.h"
#include "USART.h"
#include "stdio.h"
//#include "stm32f4xx_gpio.h"
//#include "stm32f4xx_rcc.h"
void GPIO_init(void)
{
//使能外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//设置GPIOA引脚为复用功能
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
//初始化GPIOA引脚9 为复用功能输出
GPIO_InitTypeDef GPIO_InitStruct ;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP ;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
GPIO_Init( GPIOA, & GPIO_InitStruct);
//初始化GPIOA引脚10 为复用功能输入
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 ;
GPIO_Init( GPIOA, & GPIO_InitStruct);
}
void USART_init(void)
{ //使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) ;
//初始化USART1
USART_InitTypeDef USART_InitStruct ;
USART_InitStruct.USART_BaudRate = 115200 ;
USART_InitStruct.USART_WordLength = USART_WordLength_8b ;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ;
USART_InitStruct.USART_Mode = USART_Mode_Tx ;
USART_InitStruct.USART_Parity = USART_Parity_No ;
USART_InitStruct.USART_StopBits = USART_StopBits_1 ;
USART_Init(USART1, &USART_InitStruct);
//使能USART1
USART_Cmd(USART1, ENABLE);
}
int fputc(int ch , FILE*f)
{
USART_SendData(USART1, (uint8_t) ch) ;
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) ; //等待发送完成 一直为0 则一直循环
return ch ;
}
3.3 main.c
#include "stm32f4xx.h"
#include "USART.h"
void delay(uint32_t time) ;
int main()
{
GPIO_init();
USART_init();
while(1)
{
printf("hello world\r\n") ;
}
}
4. 关于printf函数,scanf函数 重定向问题
MicroLib是缺省c库的备选库,它可装入少量内存中,与嵌入式应用程序配合使用,且这些应用程序不在操作系统中运行。