当前位置:   article > 正文

STM32 深入串口通信UART_gpio 采样率

gpio 采样率

STM32串口通信(STM32F103/STM32F407)

1.GPIO引脚复用AF机制
2.模块Clock时钟树,使能机制。(低功耗)
3.UART串口通信机制
4.NVIC中断配置机制
5.DMA搬移机制
6.Freertos串口
7.RTOS 串口中断设置

选择USART RX TX 引脚

  1. GPIO 口复用 机制
    微控制器 I/O 引脚通过一个复用器连接到板载外设/模块,该复用器一次仅允许一个外设的复
    用功能 (AF) 连接到 I/O 引脚。这可以确保共用同一个 I/O 引脚的外设之间不会发生冲突。
    在这里插入图片描述
    进行配置:
    ● 完成复位后,所有 I/O 都会连接到系统的复用功能 0 (AF0)。
    ● 外设的复用功能映射到 AF1 至 AF13。
    ● Cortex™-M4F EVENTOUT 映射到 AF15

在这里插入图片描述
PA9
在这里插入图片描述
PA10
在这里插入图片描述
复用端口的意义是SOC外设或者系统的模块的i/o功能有多路,需要配置到具体的引脚输出
引脚只是模块的输出通路,是工具人。
端口复用在datesheet中有描述,什么模块的端口事实上是指定的
在这里插入图片描述
PA9的复用AF7是串口TX。AF7系列是USART和I2S系列 引脚。
USART1 TX,RX的其中一条通路是PA9,PA10
另外一条通路是PB5 ,PB7
在这里插入图片描述

在这里插入图片描述
对于模块而言其功能的实现有多个PIN通路

在这里插入图片描述
对于PIN而言,他可能被多个模块链接

PIN越多可以同时实现模块功能就越多,复用通路只有一条,一但被选择那么其他的通路就无法联通。所以PIN越多,提供的外设能力就越大。

本节关键 Alternate function mapping
复用端口的配置
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF)

在这里插入图片描述
实现USART功能的复用需要选择复用AF7

	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
  • 1
  • 2

配置PA9,PA10的串口功能

对于STMF103系列,没有AF功能配置通过下面的 配置。
在这里插入图片描述

2.模块时钟使能
在这里插入图片描述
三条总线AHB,APB1,APB2,模块挂在相应的总线上。没有用到的模块就不需要开启,所以每个模块都有单独的时钟使能。
挂载哪条总线上就有哪个控制/管理
AHB1所管理的模块
在这里插入图片描述
AHB2 所管理的模块
在这里插入图片描述
APB管理的设备
在这里插入图片描述
APB2 管理的设备时钟
在这里插入图片描述
数据手册系统架构章节,对每条总线上的设备进行了列表,可以直接查询。
多数情况下,在库函数里面查看会更加方便
在这里插入图片描述
3.串口通信机制

在这里插入图片描述

UART 模块的输入和输出

TX发送口,RX接收口

发送的数据从TX发出从RX口接收。所以数据要发送通过本模块的TX。数据要接收通过本模块的RX。
所以两个串口设备的连线入上图所示。
人说话,从口出,听声音从耳入。
我说话,从口出,声音进别人耳朵,别人听到。
逻辑非常清晰。线不能链接错

MCU串口模块属于CPU外设。在MCU片内,电平TTL机制
TTL电平范围小,不利于长距离传输,可以通过提高传输电压提高抗干扰能力常用RS232电平。
在这里插入图片描述
电平机制的转换一般MCU无法实现,所以需要中间的转换IC
线路的联通需要物理接口,所以又会有接口标准
在这里插入图片描述
接口和电平会随着时代的发展而变化,和使用的场景关系非常大。
很显然他们都是串口通信的一部分。所以通信具有分层机制
接口和电平属于物理层。

传输
在这里插入图片描述
起始位,告知接收方数据开始发送,原则是和无信号的时候要有区别。约定无信息传递时高电平。开始传递时给低电平,表示开始
开始位S本身不不带有传输的信息,是告知对方“后面数据来了,请开始接收”
可以互相约定数据一次传输多少位 可以为5,6,7,8一般而言选择8位,代表一个字节。
发送完成数据后,需要表示传输完成,那么前面约定无信息传递时高电平。所以配备一个停止位T。
就这样重复的一个字节一个字节的传输。其实是一个Bit一个Bit传输。
为了适应跟多的情况,传输数据位可以配置,停止位也可以配置长度,需要双方约定好即可。

USART模块
可配置为 16 倍过采样或 8 倍过采样,因而为速度容差与时钟容差的灵活配置提供了可能
传输检测标志:
— 接收缓冲区已满
— 发送缓冲区为空
— 传输结束标志

十个具有标志位的中断源:
— CTS 变化
— LIN 停止符号检测
— 发送数据寄存器为空
— 发送完成
— 接收数据寄存器已满
— 接收到线路空闲
— 溢出错误
— 帧错误
— 噪声错误
— 奇偶校验错误

在这里插入图片描述
TX ,RX外部引脚

发送数据,由外部(CPU、DMA)写入数据到发送数据寄存器(TDR),按位发送到TX端口
接收数据,RX数据按位移入接收移位寄存器,然后写入RDR,可以由CPU或者DMA读走。
TDR,RDR都是8位的,所以读满就要取走
在这里插入图片描述
发送控制器,接收控制器分别控制模块的 发送和接收功能。
中断控制识别传输的各种事件,上报中断待处理。

当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧 (一个数据帧长度的高电平),接下
来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状
态寄存器 (USART_SR) 的 TC 位为 1,表示数据传输完成,如果 USART_CR1 寄存器的 TCIE 位置
1,将产生中断。
空闲帧
在这里插入图片描述

在这里插入图片描述
从起始位开始到结束位结束,测量时间大概87us 。波特率115200. 理论时间((1/115200 ) * 10 bit =86.8us)
这个时间87us是一帧的时间
在这里插入图片描述
空闲帧的开始是TX拉低的时候,也是大概87us
在发送数据时,编程的时候有几个比较重要的标志位我们来总结下。
在这里插入图片描述
如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索
起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成
后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置 1,同时如果
USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。
在这里插入图片描述
为得到一个信号真实情况,需要用一个比这个信号频率高的采样信号去检测,称为过采样,这个
采样信号的频率大小决定最后得到源信号准确度,一般频率越高得到的准确度越高,但为了得到
越高频率采样信号越也困难,运算和功耗等等也会增加,所以一般选择合适就好。
在这里插入图片描述
在测量一个bit周期的时候,测量值8.91us 和理论时间8.68存在误差,这是由于采样频率造成的。频率越高就越能还原原始信号。
STM32F407 UART 可以进行16或者8倍的过采样频率。
传输完成或者接收完成需要通知外界,一般使用中断
在这里插入图片描述
也就是说,USART内部产生事件,要不要传递出去到NVIC中断控制器,由各自的使能位决定。

ARM cortex M4 NVIC中断控制器
在这里插入图片描述
USART 模块属于peripherals ,内部的事件使能后,以IRQs的形式传达给NVIC。

typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be an enumerator of @ref IRQn_Type 
                                                   enumeration (For the complete STM32 Devices IRQ Channels
                                                   list, please refer to stm32f4xx.h file) */

  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref MISC_NVIC_Priority_Table
                                                   A lower priority value indicates a higher priority */

  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref MISC_NVIC_Priority_Table
                                                   A lower priority value indicates a higher priority */

  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

NVIC_IRQChannel

USART1_IRQn
在这里插入图片描述
上图称之为中断索引,当中断可以处理的时候,会通过索引去中断向量表上找到处理函数入口。

USART1 所有的中断,都是以USART1_IRQn的方式上报给NVIC。
USART内部的中断非常多,如何识别,通过
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
{
  uint32_t bitpos = 0x00, itmask = 0x00, usartreg = 0x00;
  ITStatus bitstatus = RESET;
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_GET_IT(USART_IT)); 

  /* The CTS interrupt is not available for UART4 and UART5 */ 
  if (USART_IT == USART_IT_CTS)
  {
    assert_param(IS_USART_1236_PERIPH(USARTx));
  } 
    
  /* Get the USART register index */
  usartreg = (((uint8_t)USART_IT) >> 0x05);
  /* Get the interrupt position */
  itmask = USART_IT & IT_MASK;
  itmask = (uint32_t)0x01 << itmask;
  
  if (usartreg == 0x01) /* The IT  is in CR1 register */
  {
    itmask &= USARTx->CR1;
  }
  else if (usartreg == 0x02) /* The IT  is in CR2 register */
  {
    itmask &= USARTx->CR2;
  }
  else /* The IT  is in CR3 register */
  {
    itmask &= USARTx->CR3;
  }
  
  bitpos = USART_IT >> 0x08;
  bitpos = (uint32_t)0x01 << bitpos;
  bitpos &= USARTx->SR;
  if ((itmask != (uint16_t)RESET)&&(bitpos != (uint16_t)RESET))
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }
  
  return bitstatus;  
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

这样可以获知USART内部具体是发生了什么事件而产生中断,在去做对应的处理。
NVIC所管理的称为全局中断。
NVIC的使能比较简单,因变对NVIC来说只有USART1_IRQn.不需要关注USART内部的各种事件。

	NVIC_InitStructure_uart.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure_uart.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure_uart.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure_uart.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure_uart);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

定义中断处理函数
在启动文件中 ; External Interrupts 写明了USART产生的中断服务函数 叫 USART1_IRQHandler
我们可以去修改这个函数名称,不过不建议这么做,保持S文件的原始性,也方便代码移植。
可以通过define的方式来定义中断服务函数。
在这里插入图片描述
一般来说,接收数据完成后会产生中断,这个时候把数据从寄存器中拿出来,放入我们准备好的缓存当中,这个时候数据是一个字节。
一个字节对于我们来说可能是一段话的而一部分,需要等待多个字节来组合一段有意义的内容。
所以把它占时放在缓存中
我们可以在缓存中对数据进行判断,识别数据包,或者一句话等等

unsigned char buff_index=0;
void USART1_IRQHandler(void)
{
	
	uint8_t ucTemp;
	
	 if (USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) {
		ucTemp = USART_ReceiveData( USART1 );
		UART1_BUFFER[buff_index]  = ucTemp;
	 	buff_index++;
		if(buff_index > UART1_BUFFER_SIZE)
		{
			buff_index = 0;
			memset(UART1_BUFFER,0,sizeof(UART1_BUFFER));
		}
		
	 }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述

启用在线调试,观察UART1_BUFFER的内容。
0x0D(ascii码是13) 指的是“回车” \r是把光标置于本行行首
0x0A(ascii码是10) 指的是“换行” \n是把光标置于下一行的同一列

DMA数据搬移

在这里插入图片描述
串口的USART框图如图所示。数据寄存器(发送数据,接收数据寄存器)只能通过CPU或者DMA访问。
CPU访问数据寄存器的流程:数据寄存器接收满或者发送完毕会产生事件,事件传入NVIC中断模块后上报给CPU,CPU通过中断服务读取。这条链路在执行就会使用CPU,增加系统的负担。如果通过DMA可以在CPU不参与的情况下自己搬运数据,就好像不执行指令就能
做事一样。DMA模块本身的内容比较复杂,这里注重实现。
在这里插入图片描述
DMA的初始化结构体比较长,最后会附上dma的初始化代码。

DMA_InitTypeDef dma_init_struct;
dma_init_struct.DMA_Channel = DMA_Channel_4;

在这里插入图片描述
DMA的信号通道选择和GPIO的复用结构有点相识。
DMA本身只管理STREAM0~STREAM7,只有这8条通路,然后STREAMx(x = 1,2,3,…8)通过多路复用器,和REQ_STRx_CH链接。
REQ_STRx_CH负责链接到具体的外设。
在这里插入图片描述

串口USART1 RX在数据流5(STREAM5)的通道4上。所以dma_init_struct.DMA_Channel = DMA_Channel_4;
初始化USART1 的代码如下所示

static void DMA_config(void)
{
	DMA_InitTypeDef dma_init_struct;
	extern unsigned char UART1_BUFFER[120];
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	DMA_DeInit(DMA2_Stream5);
	while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) {
	 }

	dma_init_struct.DMA_Channel = DMA_Channel_4;
	dma_init_struct.DMA_BufferSize = 120;
	dma_init_struct.DMA_DIR = DMA_DIR_PeripheralToMemory;
	
	dma_init_struct.DMA_FIFOMode = DMA_FIFOMode_Disable;
	dma_init_struct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	
	dma_init_struct.DMA_Memory0BaseAddr = (unsigned int)UART1_BUFFER;
	dma_init_struct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	
	dma_init_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	dma_init_struct.DMA_PeripheralDataSize =DMA_PeripheralDataSize_Byte;
	dma_init_struct.DMA_MemoryInc = DMA_MemoryInc_Enable ;
	dma_init_struct.DMA_PeripheralInc =DMA_PeripheralInc_Disable ;
	dma_init_struct.DMA_Mode =DMA_Mode_Circular;
	dma_init_struct.DMA_PeripheralBaseAddr =(USART1_BASE+0x04);
	dma_init_struct.DMA_PeripheralBurst =DMA_PeripheralBurst_Single;

	
	dma_init_struct.DMA_Priority = DMA_Priority_Medium;


	DMA_Init(DMA2_Stream5, &dma_init_struct);

	DMA_Cmd(DMA2_Stream5, ENABLE);
	while (DMA_GetCmdStatus(DMA2_Stream5) != ENABLE) {
	 }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

在这里插入图片描述
把外围 设备的数据放到内存的BUFFER

dma_init_struct.DMA_DIR = DMA_DIR_PeripheralToMemory;
  • 1

第一步把DR数据寄存器的数据取出来
1.DR寄存器地址

dma_init_struct.DMA_PeripheralBaseAddr =(USART1_BASE+0x04);
  • 1

2.一次取多少数据,总共取多少次,取满次数怎么办

dma_init_struct.DMA_PeripheralDataSize =DMA_PeripheralDataSize_Byte;
dma_init_struct.DMA_BufferSize = 120;
dma_init_struct.DMA_Mode =DMA_Mode_Circular;
  • 1
  • 2
  • 3

3。下一次取要不要取另外地址的数据

dma_init_struct.DMA_PeripheralInc =DMA_PeripheralInc_Disable ;

  • 1
  • 2

第二部 DMA获得数据
第三部 DMA写入数据到目标内存

1.内存地址

dma_init_struct.DMA_Memory0BaseAddr = (unsigned int)UART1_BUFFER;
  • 1

2.写入地址会不会变化

dma_init_struct.DMA_MemoryInc = DMA_MemoryInc_Enable ;
  • 1

根据以上的逻辑可以理清楚DMA结构体初始化的过程。
最后在主函数中使能 UART DMA

USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
  • 1

在这里插入图片描述
在这里插入图片描述
1.对面发送端的TX完成了一帧的数据传输,USART 接收完成标志RXNE置位,
2.DMA收到请求源请求
3,读取USART DR寄存器数据值
在这里插入图片描述
关掉CPU搬移数据。
在这里插入图片描述
最后开启调试,全速运行。

操作系统(Freertos)下的UART

OS下的USART

UART的数据接收是通过硬件进行,硬件没有缓存,所以一但接收数据寄存器满,就要及时的取出来
这个时候有两种方法
1.NVIC 中断服务函数处理

void USART1_IRQHandler(void)
{
	#if 1	
	uint8_t ucTemp;
	
	 if (USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) {
		ucTemp = USART_ReceiveData( USART1 );
		UART1_BUFFER[buff_index]  = ucTemp;
	 	buff_index++;
		if(buff_index > UART1_BUFFER_SIZE)
		{
			buff_index = 0;
			memset(UART1_BUFFER,0,sizeof(UART1_BUFFER));
		}
		
	 }
	#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

串口没有包的概念,所以判断数据流的起停,本身是没有办法的。
每一次接收1byte 都会产生中断
中断内容至少包含数据搬移,一般放入FIFO中
FIFO主要作用是做数据缓存
那么如何做数据的解析,如果每次收到1byte都唤起应用层任务,显然 是不明智的
在没有流量控制引脚的状态下
1.做包协议,规定包头包尾,在每次拿到数据后检查包传输是否完成。完成后,发送信号量通知应用层任务。
2.规定一次传输的字节数,中断计数。计数完成后发送信号量通知应用层任务。

void uart_rx_isr(uart_device_t *dev)
{
	int32_t ch = -1;

	if (!dev)
	{
		return;
	}

	hw_interrupt_disable();
	while (1)
	{
		ch = dev->ops->getc(dev);
		if (ch == -1)
		{
			break;
		}
		ch &= 0xFF;
		sw_fifo_put(&dev->rx_fifo, (const uint8_t *)&ch, 1, SW_FIFO_TYPE_COVER);
		if (dev->rx_indicate.cb)
		{
			dev->rx_indicate.cb(dev->rx_indicate.para);
		}
	}
	hw_interrupt_enable();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

dev->rx_indicate.cb是一个回调函数,当接收数据后执行一个函数,这个时候可以做数据的处理或者发送信号量。

2.使用DMA处理


数据从DR寄存器拿出来后放入指定的缓存,这个时候是否要通知CPU来对数据进行判别
数据是否可用。串口UART是基本的通信,它只有帧的概念 也就是起始位和结束位之间的数据。
没有数据包的概念。起始位和结束位之间的数据一般是一个字节
在这里插入图片描述
DMA传输有事件中断
在这里插入图片描述
开启相应的中断,在中断函数中处理搬运。

static void DMA_config(void)
{
	DMA_InitTypeDef dma_init_struct;
	extern unsigned char UART1_BUFFER[120];
	NVIC_InitTypeDef DMA_IT_init;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	DMA_DeInit(DMA2_Stream5);
	while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) {
	 }

	dma_init_struct.DMA_Channel = DMA_Channel_4;
	dma_init_struct.DMA_BufferSize = 20;
	dma_init_struct.DMA_DIR = DMA_DIR_PeripheralToMemory;
	
	dma_init_struct.DMA_FIFOMode = DMA_FIFOMode_Disable;
	dma_init_struct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	
	dma_init_struct.DMA_Memory0BaseAddr = (unsigned int)UART1_BUFFER;
	dma_init_struct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	
	dma_init_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	dma_init_struct.DMA_PeripheralDataSize =DMA_PeripheralDataSize_Byte;
	dma_init_struct.DMA_MemoryInc = DMA_MemoryInc_Enable ;
	dma_init_struct.DMA_PeripheralInc =DMA_PeripheralInc_Disable ;
	dma_init_struct.DMA_Mode =DMA_Mode_Circular;
	dma_init_struct.DMA_PeripheralBaseAddr =(USART1_BASE+0x04);
	dma_init_struct.DMA_PeripheralBurst =DMA_PeripheralBurst_Single;

	
	dma_init_struct.DMA_Priority = DMA_Priority_Medium;


	DMA_Init(DMA2_Stream5, &dma_init_struct);

	DMA_Cmd(DMA2_Stream5, ENABLE);
	while (DMA_GetCmdStatus(DMA2_Stream5) != ENABLE) {
	 }

	DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);

	DMA_IT_init.NVIC_IRQChannel=DMA2_Stream5_IRQn ;
	DMA_IT_init.NVIC_IRQChannelCmd=1;
	DMA_IT_init.NVIC_IRQChannelPreemptionPriority=1;
	DMA_IT_init.NVIC_IRQChannelSubPriority = ENABLE;

	NVIC_Init(&DMA_IT_init);
	
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

串口部分的设计如果每次传输1byte都进行中断,或者没 1byte进行字符解析,会占用大量的CPU资源。
使用DMA可以非常高效的解决CPU使用率的问题

RTOS 串口中断设置(Freertos为例)

操作系统在进行关键性操作的时候需要进入临界代码断,其目的是在进行内核数据读写的时候,保证内核数据不被篡改以及数据读写完整性。简单说就是操作系统的一些API在执行的时候会关掉一部分中断。

关掉的中断,会有一个最大优先级。为什么不完全关掉中断呢,为了保证高优先级的中断服务不被打断,减少抖动。

因此操作系统都会有一个 OS可屏蔽最大中断优先级。
意思就是在操作系统进行关键性操作的时候,会关掉这个中断优先级以下的中断。
同样,高于这个优先级的中断中不能调用OS API 函数。

也就是说 串口的中断优先级设定不可以高于 OS可屏蔽最大中断优先级

以freertos 为例
configKERNEL_INTERRUPT_PRIORITY 为systick 的中断优先级
configMAX_SYSCALL_INTERRUPT_PRIORITY 为 OS可屏蔽最大中断优先级

在这里插入图片描述
NVIC_IRQChannelPreemptionPriority > configMAX_SYSCALL_INTERRUPT_PRIORITY

中断优先级来说配置的数字越小,优先级越高。必须满足上面的不等式。

首先一个中断发生后Cortex M3 内核PSR寄存器
在这里插入图片描述
ISR记录了中断号,也就是中断向量表的索引位置。

中断向量表的53号中断

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1_2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

53号是USART1_IRQHandler ,然后从优先级寄存器中读取 优先级和OS可屏蔽的最大优先级做比较。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/529944
推荐阅读
相关标签
  

闽ICP备14008679号