STM32 寄存器配置笔记——USART配置中断接收乒乓缓存处理

一、概述

       本文主要介绍如何配置USART接收中断,使用乒乓缓存的设计接收数据并将其回显在PC 串口工具上。以stm32f10为例,配置USART1 9600波特率。具体配置参考上一章节STM32 寄存器配置笔记——USART配置 打印。

        乒乓缓存的设计应用场景:当后面的处理单元在工作期间,前面的 buffer 的内容不能被释放。或者,在处理单元工作期间, buffer 的特定地址的内容不止被访问一次。优点是相比全部写完再读的方式,可以节约读写时序。

二、中断优先级


       STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。即抢占优先级和响应优先级。

抢占
抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由NVIC_IRQChannelPreemptionPriority 的参数配置。

响应
响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断, 响应属性由NVIC_IRQChannelSubPriority 参数配置。
 

三、配置流程

        1)使能RXNE中断

        接收缓冲区非空中断使能,通过MY_NVIC_init函数配置中断的抢占优先级和响应优先级为3(这里可以随意配置本章重点不在这)。最后一个参数表示分组为2。即有2位用来表示抢占优先级,2位用来表示响应优先级。

    USART1->CR1 |= 1 << 5;                                // enable RXNE interrupt 
	MY_NVIC_Init(3,3,USART1_IRQn,2);                      // 2bit PreemptionPriority 3 2bit SubPriority 3

        2)编写串口中断函数

        USART1_IRQHandler是在启动文件里定义的只要有串口中断便会跑进来。查询状态寄存器USART_SR的RXNE位为1则表示收到数据可以读出。此时从USART_DR寄存器读取数据。

      三、乒乓缓存设计

         1)数据结构定义

typedef struct
{
    u8 USART_RX_BUF[USART_REC_LEN];
	u16 len;
} USART_DATA;

typedef struct
{
   USART_DATA stUartFifo[2];
   u8 curRecFifo;
} USART_HANDLE;

static USART_DATA *p_cur_Usart1_Handle = NULL;
static USART_DATA *p_cur_Data_ProcessingHandle = NULL;
static USART_HANDLE g_Usart1Handle;

uin16 needPro = 0;

        2)乒乓缓存初始化 

void init_usart_handle(void)
{
	memset((u8*)&g_Usart1Handle, 0, sizeof(USART_HANDLE));
    p_cur_Usart1_Handle = &g_Usart1Handle.stUartFifo[0];
}

        3)乒乓缓存接收

void USART1_IRQHandler(void)
{
    if(USART1->SR&(1<<5))
	{
		p_cur_Usart1_Handle->USART_RX_BUF[p_cur_Usart1_Handle->len++] = USART1->DR;
	}
}

         4)乒乓缓存切换

        这里是以每收到一帧完整帧的数据都是以0x0d 0x0a结尾的数据为例。收到一帧完整帧将当前乒乓缓存A切换为处理,乒乓缓存B切换为接收。

void change_curFifo(void)
{
    u16 len = p_cur_Usart1_Handle->len;
	if (len >= 2)
	{
		if (p_cur_Usart1_Handle->USART_RX_BUF[len - 2] == 0x0d 
			&& p_cur_Usart1_Handle->USART_RX_BUF[len - 1] == 0x0a)
		{
			printf("curFifo:%d len:%d\r\n", g_Usart1Handle.curRecFifo, len);
			p_cur_Data_ProcessingHandle = p_cur_Usart1_Handle;
			g_Usart1Handle.curRecFifo++;
			p_cur_Usart1_Handle = &g_Usart1Handle.stUartFifo[g_Usart1Handle.curRecFifo % 2];
			p_cur_Usart1_Handle->len = 0;
            needPro = 1;
		}

	}
}

        5)乒乓缓存处理

        打印接收内容。

void data_processing(void)
{
    if (needPro)
	{
        needPro = 0;
		p_cur_Data_ProcessingHandle->USART_RX_BUF[p_cur_Data_ProcessingHandle->len] = 0;
	    printf("func:%s dat:%s\r\n", __func__, p_cur_Data_ProcessingHandle->USART_RX_BUF);
	}
}

      6)调用如下

      主循环一直轮询是否需要切换乒乓缓存以及是否存在数据待处理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用STM32F103的USART1,通过DMA方式接收和发送数据的配置及实现函数: 1. 配置USART1 首先需要配置USART1的引脚、波特率、数据位、停止位等参数。在这里我们假设USART1的引脚已经正确连接。以下是典型的USART1配置代码: ```c // 使能USART1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // USART1 GPIO 配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // USART1 配置 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); // 使能USART1 USART_Cmd(USART1, ENABLE); ``` 2. 配置DMA 接下来需要配置DMA,以便能够使用DMA方式进行数据传输。在这里我们使用DMA1的通道4来接收数据,使用通道5来发送数据。以下是DMA配置代码: ```c // 使能DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 配置DMA接收 DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buf; // 接收缓冲区 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = RX_BUF_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); DMA_Cmd(DMA1_Channel5, ENABLE); // 配置DMA发送 DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tx_buf; // 发送缓冲区 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 0; // 初始发送长度为0 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 发送模式为正常模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // 使能发送完成中断 ``` 3. 编写DMA中断服务函数 在使用DMA进行数据传输时,需要编写DMA中断服务函数。在这里我们需要编写发送完成中断服务函数和接收完成中断服务函数。以下是中断服务函数的代码: ```c // DMA1 Channel4 发送完成中断服务函数 void DMA1_Channel4_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC4) != RESET) { // 清除中断标志位 DMA_ClearITPendingBit(DMA1_IT_TC4); // 关闭DMA发送通道 DMA_Cmd(DMA1_Channel4, DISABLE); // 发送完成回调函数 if (tx_complete_callback != NULL) tx_complete_callback(); } } // DMA1 Channel5 接收完成中断服务函数 void DMA1_Channel5_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC5) != RESET) { // 清除中断标志位 DMA_ClearITPendingBit(DMA1_IT_TC5); // 接收完成回调函数 if (rx_complete_callback != NULL) rx_complete_callback(); } } ``` 4. 实现发送和接收函数 最后需要实现发送和接收函数,以便能够使用DMA方式进行数据传输。以下是典型的发送和接收函数代码: ```c // 发送函数 void usart1_dma_send(uint8_t *buf, uint16_t len) { if (len == 0) return; // 等待DMA发送通道空闲 while (DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET); // 设置发送长度 DMA_SetCurrDataCounter(DMA1_Channel4, len); // 设置发送缓冲区地址 DMA_SetMemoryBaseAddr(DMA1_Channel4, (uint32_t)buf); // 使能DMA发送通道 DMA_Cmd(DMA1_Channel4, ENABLE); } // 接收函数 uint16_t usart1_dma_receive(uint8_t *buf, uint16_t len) { uint16_t rx_len = RX_BUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5); if (rx_len == 0) return 0; if (rx_len > len) rx_len = len; // 复制接收缓冲区数据 memcpy(buf, rx_buf, rx_len); // 重新启动DMA接收通道 DMA_Cmd(DMA1_Channel5, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel5, RX_BUF_SIZE); DMA_Cmd(DMA1_Channel5, ENABLE); return rx_len; } ``` 以上就是使用STM32F103的USART1,通过DMA方式接收和发送数据的配置及实现函数。注意,以上代码仅供参考,具体实现需要结合实际应用场景进行相应的修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值