就不说什么同步通讯了,一般也都是用的异步串口通讯。
只需要两个线,RX,TX。
连接方式是 1设备的RX接2设备的TX,1设备的TX接2设备的RX
image-1676781077643

这里只说串口一,其他串口一样的
串口需要的参数:

起始位
数据位(8位或9位【没有校验位】)
奇偶校验位(第九位)
停止位(1,1.5,2位停止位)
波特率
image-1676784609709

初始化

时钟初始化,串口1的时钟在APB2上,其他串口的都在APB1上

时钟初始化

image-1676857766240

RCC->APB2ENR|=1<<2;     //使能PORTA口时钟
RCC->APB2ENR|=1<<14;    //使能串口时钟

初始化引脚

RX(PA10)输入,TX(PA9)输出
空数据的时候是高电平 所以RX用上拉输入 (CRH:1000)
(有搜到这样的话:RX不上拉,悬空的话,电路的某些信号影响(比如射频,大功率器件),就容易误触发,进入接收中断。)
TX就复用推挽输出 (CRH:1011)【不知道为什么是这个数,看这里,CRL,CRH端口配置寄存器】

GPIOA->CRH&=0XFFFFF00F;//PA9,PA10 CRH寄存器清0
GPIOA->CRH|=0X000008B0;//PA9,PA10 CRH寄存器置数

复位

外设有异常的时候通过复位来恢复,一般初始化的时候就复位一次。(复位写一后记得写回0)
image-1676857903688

RCC->APB2RSTR|=1<<14;      //复位串口1
RCC->APB2RSTR&=~(1<<14);//停止复位

波特率设置

image-1676858071692

接收器和发送器的波特率在USARTDIV的整数和小数寄存器中的值应设置成相同。
524/754
Tx / Rx 波特率 = FCK(16USARTDIV)\frac{{F_{CK}}}{(16*USARTDIV)}
这里的fCK是给外设的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1)
USARTDIV是一个无符号的定点数。这12位的值设置在USART_BRR寄存器。
注: 在写入USART_BRR之后,波特率计数器会被波特率寄存器的新值替换。因此,不要在通信进行中改变波特率寄存器的数值。

举例子

假设串口1要115200的波特率
USARTDIV=FCK(16波特率)USARTDIV=\frac{{F_{CK}}}{(16*波特率)}
72000000(16115200)=39.0625\frac{{72000000}}{(16*115200)}=39.0625
整数部分:DIV_Mantissa=39=0X27;
小数部分:DIV_Fraction=16*0. 0625=1=0X01;
合起来给BBR寄存器的:0X0271

float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp;//得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;
mantissa+=fraction;

这是常用的波特率和给出的计算结果。
image-1676859305744

设置控制寄存器

控制寄存器有3个,USART_CR1,CR2,CR3,这里只看CR1
image-1676968678296image-1676968697765image-1676968719663image-1676968750958
关于CR2,可能用到设置停止位,如果你不想1位停止位,去CR2设置

这里的代码就是使能了发送接收,接收中断,和USART模块,对应上面的表看就行

USART1->CR1|=0X202C;   //1位停止,无校验位,使能接收中断. 0010 0000 0010 1100

设置中断

上面我们使能了接收中断,那么下面要开启串口1的NVIC中断,
setpriority函数(中断源,抢占优先级,相应优先级)

    HAL_NVIC_SetPriority(USART1_IRQn, 2, 2);
    HAL_NVIC_EnableIRQ(USART1_IRQn);

image-1676969372514

中断函数(HAL库)

USART1_IRQHandler函数,在USART1产生中断后,就会跳到这里来执行。想写啥就写啥
HAL_UART_IRQHandler(&huart1);会清除中断标志,
后面要继续用中断接收的话,要重新用一次HAL_UART_Receive_IT
image-1676992948190

使用(HAL库)

关于中断,HAL库中, 中断可以使用USARTx_IRQHandler(),也可以用HAL_UART_RxCpltCallback()
他两个什么区别我也说不来,大概是IRQHandler后面会进行HAL_UART_RxCpltCallback
而HAL_UART_RxCpltCallback是多个UART共用的。
IRQHandler没有__wike修饰,不能重写,所以要去it.c里面修改
RxCpltCallback被__wike修饰,可以重写,但用多个uart时判断麻烦。
结论:

  1. 如果只用一个uart,用RxCpltCallback
  2. 如果用多个uart,用IRQHandler

接收任意长度字符并发送回去

初始化都自动生成的,记得在初始化里面加入或者找其他地方加入初始化中断接收

HAL_UART_Receive_IT(&huart1,(uint8_t *)&g_rx_buffer,1);

一次接收一个字节比较灵活。
image-1677152641034

定义变量

uint8_t g_rx_buffer[1];			//保存的每一次中断接收到的数据,做一个中转
uint8_t g_usart_rx_sta = 0;		//用16位数据记录保存到的数据的状态
uint8_t g_usart_rx_buff[256];		//最终保存的数据,可以设置大点2^8=256
int len,times;

回调函数(接收):

image-1677152594424

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance==USART1)			//判断是否是usart1来的中断
	{
		if((g_usart_rx_sta &0x8000 )== 0)		//判断是否接收到过/n	(1000 0000 0000 0000)
		{
			if(g_usart_rx_sta &0x4000)		//没接收到过/n,判断是否接收到过/r	(0100 0000 0000 0000)
			{
				if(g_rx_buffer[0] != 0x0A)	//接收到过/r,判断当前数据是否是/n
				{
					g_usart_rx_sta=0;	//不是/n,接收了一半回车,有问题,数据不要了
				}
				else
				{
					g_usart_rx_sta |=0x8000;	//是/n,让最高位置1
				}
			}
			else	//没接收到过/r
			{
				if(g_rx_buffer[0] == 0x0D)	//判断当前数据是否为\r
				{
					g_usart_rx_sta |=0x4000;	//是的话把次高位置1
				}
				else	//不是\n,是正常的数据,记下来
				{
					g_usart_rx_buff[g_usart_rx_sta & 0x3FFF]=g_rx_buffer[0];
					g_usart_rx_sta++;
					
					if(g_usart_rx_sta>(256-1))	//数据量过大,清零
					{
						g_usart_rx_sta=0;
					}
					
				}
			}
		}
	}
	HAL_UART_Receive_IT(&huart1,g_rx_buffer,1);
}

发送数据

当g_usart_rx_sta最高位为1的时候,发送数据

void uart1_t()
{
	if(g_usart_rx_sta & 0x8000)
	{
		len = g_usart_rx_sta & 0x3fff;
		printf("长度:%d",len);
		printf("\r\n这次发送的数据:\r\n");
		HAL_UART_Transmit(&huart1,g_usart_rx_buff,len,200);
		printf("\r\n---------换行-----------\r\n");
		g_usart_rx_sta=0;
	}
}

重写printf

其实没啥用,不过我觉得挺方便的
有两种写法,建议不用半主机
半主机是作用于ARM目标的一种机制,可以将来自STM32单片机应用程序的输入与输出请求传送至运行仿真器的PC主机上。使用此机制可以启用C库中的函数,如printf()和scanf()等输入与输出函数,使用PC主机的屏幕和键盘。
建议看这里的

半主机模式

#include <stdio.h>


int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 50);
  return ch;
}
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 50);
  return ch;
}

在usart.c中引入stdio.h库,然后重写一下fputc,
image-1677040943469
选择上使用微库,连上SWD
就可以用printf了
这是半主机模式的,没有SWD连接到电脑的话,程序会卡死