①串口引脚

选择PA9和PA10充当串口的发送引脚和接收引脚。

②中断控制器NVIC配置

void Usart1_NVICInit(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
	
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
}

③串口的初始化配置

初始化GPIO和USART,引脚复用并且接收中断和串口使能。

void Usart1_Init(uint32_t BaudRate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;
	
	USART_InitStructure.USART_BaudRate = BaudRate;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	USART_Init(USART1,&USART_InitStructure);
	
	Usart1_NVICInit();
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	USART_Cmd(USART1,ENABLE);
}

注意事项

需要注意的是,不可写成

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9 | GPIO_PinSource10,GPIO_AF_USART1);

因为在 STM32 的 GPIO 初始化中,类似 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 是可以的,因为 GPIO_Init() 内部会遍历所有位,逐一配置。但 GPIO_PinAFConfig() 是专门为配置复用功能(Alternate Function)设计的,它直接操作 AFR(Alternate Function Register) 寄存器。AFR 寄存器将每个引脚的 4 位复用配置独立存放,不支持批量写入不同引脚的复用值(因为每个引脚可能配置成不同的 AF 编号)。如果强行用 | 传入多个引脚,函数无法知道你想给每个引脚分别配什么 AF 值。

④发送单个字节和字符串的函数

查看手册可知TXE(发送标志位)和RXNE(接收标志位)是硬件自动清零,但是TC(发送完成标志位)是需要软件清零的。

发送单个字节函数

单个字符发送完之后,串口输出

void Usart_SendByte(USART_TypeDef *Usart_Tx,uint8_t ch)
{
	USART_SendData(Usart_Tx,ch);
	while(USART_GetFlagStatus(Usart_Tx,USART_FLAG_TXE) == RESET);
}

发送字符串函数

注意不要写成u8或者其它

unsigned int 与一般的 uint8_t 是有区别的

uint8_t unsigned int
大小固定 固定为1字节  大小可变(通常≥2字节)
范围 0~255 0~4294967295
应用场景 需要精确控制内存布局、网络协议、硬件寄存器时 需要通用整数运算且不关心大小时

do...while:先执行后判断

指针索引,如果没有遇到字符串结束符,就一直发送单个字节。

用TC标志位来判断是因为TC 是在整个字节(包括停止位)完全发出后才置位。

记得最后清除TC标志位

void Usart_String(USART_TypeDef *Usart_Tx,char *str)
{
	unsigned int i = 0;
	do
	{
		Usart_SendByte(Usart_Tx, *(str + i));
		i++;
	}while(*(str + i) != '\0');
	while(USART_GetFlagStatus(Usart_Tx,USART_FLAG_TC) == RESET);
	USART_ClearFlag(Usart_Tx, USART_FLAG_TC);  
}

⑤串口中断函数

单片机每接收到一个完整的字节,就会进入一次串口中断函数。

如果发现接收标志位置1了,就发送接收到的字节

void USART1_IRQHandler(void)
{
	uint8_t temp;
	if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
	{
		temp = USART_ReceiveData(USART1);
		USART_SendData(USART1,temp);
	}
}

⑥主函数

int main()
{
	u8 i=0;
	SysTick_Init(168);
	Usart1_Init(9600);
	LED_Init();
	Usart_SendByte(USART1,'A');
	Usart_String(USART1,"CHINA");
	while(1)
	{
		i++;
		if(i%20==0)
		{
			LED1=!LED1;
		}
		delay_ms(10);
	}
}

⑦结果

串口输出

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐