八、Stm32学习-USART-中断与接收数据包

1.通信接口

全双工就是数据的收和发可以同时进行;半双工就是数据的收和发不能同时进行。

异步时钟是设备双方需要约定对应的波特率;同步时钟是设备双方有一根时钟线,发送或接收数据是根据这根时钟线来的。

单端电平是需要共GND;差分电平不需要共GND,它的数据传输是依靠电压差来传递的。

2.电平标准

有些时候设备两端的电压是不同的,所以高低电平的表示电压也是不同的电压表示的。这个时候就需要添加电平转换电路。以上是常见的几种串口电平。485可以传输上千米,而232与TTL可能只能几十米。这些电压都是对地电压。

3.串口的参数

1000比特就是1s要发送1000位;波特率为9600时,发送一位的时间为1/9600;

固定低电平为起始位,告诉设备我要开始发送了;

固定高电平为停止位,告诉设备我发送好了。(同时把引脚恢复成了高电平)

有3种校验:奇校验,偶校验,无校验。

奇校验就是,包括奇校验在内的9位数据会出现奇数个1;发送数据的时候,若不是奇数,校验位就会补1,若是奇数,校验位就会补0;最后接收数据的时候会检查个数是否正常,是否为奇数。

偶校验就是,包括偶校验在内的9个数据会出现偶数个1;同理。

停止位有三种:1个停止位,0.5个停止位,1.5个停止位

相当于每次发送一个字节的数据,停止位占1个字节或0.5字节或1.5字节。

串口没有发送数据的时候就是空闲高电平。

4.Stm32的USART介绍

串口的同步模式一般用于兼容其他的协议,串口一般用异步。

波特率是对时钟比如72MHZ进行分频,然后得到我们想要的波特率,就可以以这样的波特率进行收发数据了。

硬件流控制要多一根线,有点像标志位,判断对面设备是否接收完成我们发送的数据,这根线置高或低电平等。

5.USART基本结构

一旦发送移位寄存器移位完成,TDR寄存器就会把数据往下送,然后置一个标志位。我们就可以判断标志位,要不要写下一个数据等。

6.程序编写-USART-发送与接收数据包

(1)USART引脚与时钟配置

打开对应外设与引脚的时钟。配置引脚的模式,发送数据就是引脚输出高低电平,所以选择复用推挽;接收数据,查阅手册可以配置位上拉或者浮空。

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //发送数据就是置高低电平,所以选复用推挽,增加输出能力
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   //接收数据上拉或者浮空,阅读手册可知道
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

 (2)USART基本配置

	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate = 9600;  									 //波特率
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;				//发送模式
	USART_InitStruct.USART_Parity = USART_Parity_No;							//校验无
	USART_InitStruct.USART_StopBits = USART_StopBits_1;							//停止位长度为1
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;					//数据帧长度为8
	USART_Init(USART1,&USART_InitStruct);

设置波特率为9600,双方的波特率一定要相同哦。

 关闭硬件流控制。

打开发送与接收模式。

无校验。

停止位长度为1。

数据帧的长度为8。

这样我们的串口就配置好了,只差最后一步使能串口USART1

(3)配置串口中断和使能

	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef  NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
    USART_Cmd(USART1,ENABLE);

想要使用串口中断就要先打开通道。

配置中断的通道。

 (4)中断函数编写

中断函数的名字可以去系统的启动文件中查找。

void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量
	static uint8_t Rxcount =0;
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		
	{
		Serial_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量
		if(RxState==0)
		{
			if(Serial_RxData==0xFF)
			{
				RxState=1;
				Rxcount=0;
			}
		}
		else if(RxState==1)
		{
			RxData_Packet[Rxcount]=Serial_RxData;
			Rxcount++;
			if(Rxcount>=4)
			{
				RxState=2;
			}
			
		}
		else if(RxState==2)
		{
			if(Serial_RxData==0xFE)
			{
				RxState=0;
				Serial_RxFlag = 1;										//置接收标志位变量为1
			}
		}
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);			//清除USART1的RXNE标志位
																//读取数据寄存器会自动清除此标志位
																//如果已经读取了数据寄存器,也可以不执行此代码
	}
}

在中断函数中完成数据转存,然后在主函数中打印出来

(5)主函数

int main(void)
{
	OLED_Init();
	Serial_Init();
	OLED_ShowString(1, 1, "RxData:");
	
	while(1)
	{
		if(Serial_GetRxFlag()==1) //接收到数据了,判断标志位
		{
			OLED_ShowHexNum(2,1,RxData_Packet[0],2);
			OLED_ShowHexNum(2,4,RxData_Packet[1],2);
			OLED_ShowHexNum(2,7,RxData_Packet[2],2);
			OLED_ShowHexNum(2,10,RxData_Packet[3],2);
			SendPacket(RxData_Packet,4);
		}
	}
	
}

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,以下是一个完整的使用STM32G4HAL库实现串口空闲中断接收不定长数据的代码,供您参考: 首先,需要在CubeMX中配置串口和中断。在串口配置中,需要开启空闲中断,并设置接收缓冲区大小。在中断配置中,需要开启串口空闲中断,并设置中断优先级。 接下来是代码实现: ```c #include "main.h" #include "stm32g4xx_hal.h" #define RX_BUF_SIZE 64 // 接收缓冲区大小 UART_HandleTypeDef huart2; uint8_t rx_buf[RX_BUF_SIZE]; // 接收缓冲区 uint8_t rx_len = 0; // 接收数据长度 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) // 判断是哪个串口触发的中断 { if(rx_len < RX_BUF_SIZE) // 判断是否超出接收缓冲区大小 { rx_buf[rx_len++] = (uint8_t)(huart->Instance->RDR & 0xFF); // 读取接收数据 } else { rx_len = 0; // 超出接收缓冲区大小,清空缓冲区 } } HAL_UART_Receive_IT(&huart2, (uint8_t *)&rx_buf[rx_len], 1); // 重新开启接收中断 } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); HAL_UART_Receive_IT(&huart2, (uint8_t *)&rx_buf[rx_len], 1); // 开启接收中断 while (1) { if(rx_len > 0) // 判断是否接收到数据 { // 处理接收到的数据,例如打印到串口或处理指令等操作 HAL_UART_Transmit(&huart2, rx_buf, rx_len, HAL_MAX_DELAY); // 回传接收到的数据 rx_len = 0; // 清空接收缓冲区 HAL_UART_Receive_IT(&huart2, (uint8_t *)&rx_buf[rx_len], 1); // 重新开启接收中断 } } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 4; RCC_OscInitStruct.PLL.PLLN = 32; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { Error_Handler(); } } ``` 代码中使用了串口空闲中断,当接收到完整的数据包后会触发中断,然后在中断回调函数中读取接收缓冲区的数据并清空缓冲区,同时重新开启接收中断。在主循环中判断接收缓冲区是否有数据,如果有则进行处理,例如将数据回传到串口或进行其他操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值