精华内容
下载资源
问答
  • 主题思想:接收:配置串口DMA接收,打开串口的空闲中断,但是配置DMA接收的长度一定要合适,小了的话容易溢出。然后在串口的空闲中断里,关闭DMA的接收,且取出DMA数据,转存也好,直接处理也好。完了之后,再次配置...

    主题思想:

    接收:配置串口DMA接收,打开串口的空闲中断,但是配置DMA接收的长度一定要合适,小了的话容易溢出。然后在串口的空闲中断里,关闭DMA的接收,且取出DMA数据,转存也好,直接处理也好。完了之后,再次配置DMA接收,把地址指向DMA接受数组的开始。

    发送:DMA发送时,置位标记,需等发送完成中断里清零标记,防止连续调用DMA发送,造成上次没发完,这次直接更改了DMA发送的指针。导致发送不完整。

    一、CubeMX配置

    配置串口,可配置多个串口,方式相同

    配置完成后,根据使用习惯生成代码

    二、改写代码

    打开MDK,修改DMA处void MX_DMA_Init(void) 里,无需DMA接收中断,注释掉DMA接收中断,具体注释掉通道几,根据芯片来 void MX_DMA_Init(void)

    {

    /* DMA controller clock enable */

    __HAL_RCC_DMA2_CLK_ENABLE();

    /* DMA interrupt init */

    /* DMA2_Stream0_IRQn interrupt configuration */

    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 2, 0);

    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    /* DMA2_Stream2_IRQn interrupt configuration */

    // HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);

    // HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);

    /* DMA2_Stream7_IRQn interrupt configuration */

    HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 1, 0);

    HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);

    }

    在usart.h文件里定义串口接收数据类型我是如下定义的(usart.h)

    /* USER CODE BEGIN Private defines */

    #define RECEIVELEN 1024 //长度根据应用自定义

    #define USART_DMA_SENDING 1 //发送未完成

    #define USART_DMA_SENDOVER 0 //发送完成

    typedef struct

    {

    uint8_t receive_flag:1;//空闲接收标记

    uint8_t dmaSend_flag:1;//发送完成标记

    uint16_t rx_len;//接收长度

    uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存

    }USART_RECEIVETYPE;

    extern USART_RECEIVETYPE UsartType1;

    /* USER CODE END Private defines */

    3. 然后在usart.c里需要添加空闲中断处理函数,以及DMA发送函数等。(usart.c)

    1)首先定义数据类型:

    /* USER CODE BEGIN 0 */

    USART_RECEIVETYPE UsartType1;

    /* USER CODE END 0 */

    2) 然后定义空闲中断处理函数和DMA发送函数 ,串口的Idle中断函数名需要申明到uart.h文件里,方便中断文件函数里调用

    /* USER CODE BEGIN 1 */

    #ifdef __GNUC__

    /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf

    set to 'Yes') calls __io_putchar() */

    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)

    #else

    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)

    #endif /* __GNUC__ */

    PUTCHAR_PROTOTYPE

    {

    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);

    return ch;

    }

    //DMA发送函数

    void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)

    {

    while(UsartType1.dmaSend_flag == USART_DMA_SENDING);

    UsartType1.dmaSend_flag = USART_DMA_SENDING;

    HAL_UART_Transmit_DMA(&huart1, pdata, Length);

    }

    //DMA发送完成中断回调函数

    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

    {

    __HAL_DMA_DISABLE(huart->hdmatx);

    UsartType1.dmaSend_flag = USART_DMA_SENDOVER;

    }

    //串口接收空闲中断

    void UsartReceive_IDLE(UART_HandleTypeDef *huart)

    {

    uint32_t temp;

    if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))

    {

    // HAL_UART_DMAStop(&huart1);//这里不用Stop,不然会把发送一起停掉了

    HAL_UART_DMA_StopRX(huart);//改为用StopRX,这样就不影响DMA发送了

    __HAL_UART_CLEAR_IDLEFLAG(&huart1);

    temp = huart1.hdmarx->Instance->NDTR;

    UsartType1.rx_len = RECEIVELEN - temp;

    UsartType1.receive_flag=1;

    HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN);

    }

    }

    /* USER CODE END 1 */

    4. 在中断文件里添加(当然,上面的空闲中断处理函数需要声明)(stm32fxxxit.c)

    void USART1_IRQHandler(void)

    {

    /* USER CODE BEGIN USART1_IRQn 0 */

    UsartReceive_IDLE(&huart1);

    /* USER CODE END USART1_IRQn 0 */

    HAL_UART_IRQHandler(&huart1);

    /* USER CODE BEGIN USART1_IRQn 1 */

    /* USER CODE END USART1_IRQn 1 */

    }

    5. 主函数里,打开空闲中断,初始化DMA接收

    /* USER CODE BEGIN 2 */

    HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN);

    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);

    /* USER CODE END 2 */

    6. 然后就可以在while(1)里处理数据了

    /* Infinite loop */

    /* USER CODE BEGIN WHILE */

    while (1)

    {

    if(UsartType1.receive_flag)//如果产生了空闲中断

    {

    UsartType1.receive_flag=0;//清零标记

    Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//串口打印收到的数据。

    }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    }

    /* USER CODE END 3 */

    修改的代码就到这里了。但是HAL库版本更迭,可能函数名会不同。

    展开全文
  • 接收:配置串口DMA接收,打开串口的空闲中断,但是配置DMA接收的长度一定要合适,小了的话容易溢出。然后在串口的空闲中断里,关闭DMA的接收,且取出DMA数据,转存也好,直接处理也好。完了之后,再次配置DMA接收,...

    主题思想:

    接收:配置串口DMA接收,打开串口的空闲中断,但是配置DMA接收的长度一定要合适,小了的话容易溢出。然后在串口的空闲中断里,关闭DMA的接收,且取出DMA数据,转存也好,直接处理也好。完了之后,再次配置DMA接收,把地址指向DMA接受数组的开始。

    发送:DMA发送时,置位标记,需等发送完成中断里清零标记,防止连续调用DMA发送,造成上次没发完,这次直接更改了DMA发送的指针。导致发送不完整。

    一、CubeMX配置(图不是一个工程的,仅作参考)

    1. 配置串口,可配置多个串口,方式相同 串口配置DMA配置中断配置
    2. 配置完成后,根据使用习惯生成代码 生成配置

    二、改写代码(代码是F103,用了Uart1和Uart3)

    1. 打开MDK,修改DMA处void MX_DMA_Init(void) 里,无需DMA接收中断,注释掉DMA接收中断,具体注释掉通道几,根据芯片来
      void MX_DMA_Init(void) 
      {
        /* DMA controller clock enable */
        __HAL_RCC_DMA1_CLK_ENABLE();
      
        /* DMA interrupt init */
        /* DMA1_Channel2_IRQn interrupt configuration */
        HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
        /* DMA1_Channel3_IRQn interrupt configuration */
      //  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
      //  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
        /* DMA1_Channel4_IRQn interrupt configuration */
        HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
        /* DMA1_Channel5_IRQn interrupt configuration */
      //  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
      //  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
      
      }

       

    2. 在usart.h文件里定义串口接收数据类型我是如下定义的(usart.h)

    /* USER CODE BEGIN Private defines */
    #define RECEIVELEN 64
    #define USART_DMA_SENDING 1//发送未完成
    #define USART_DMA_SENDOVER 0//发送完成
    	 
    #define MODBUD_SENDMODE 1	 
    	 
    typedef struct
    {
    	uint8_t Receive:1;//空闲接收标记
    	uint8_t Send:1;//发送完成标记
    	uint16_t RxLen;//接收长度
    	uint8_t RxBuf[RECEIVELEN];//DMA接收缓存
    }UART_T;
    
    extern UART_T xtUart1,xtUart3;
    /* USER CODE END Private defines */
    
    
    /* USER CODE BEGIN Prototypes */
    void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length);
    void Usart3SendData_DMA(uint8_t *pdata, uint16_t Length);
    void UsartReceive_IDLE(UART_HandleTypeDef *huart);
    
    /* USER CODE END Prototypes */

    3. 然后在usart.c里需要添加空闲中断处理函数,以及DMA发送函数等。(usart.c)

        1)首先定义数据类型:

    /* USER CODE BEGIN 0 */
     
    UART_T xtUart1,xtUart3;
     
    /* USER CODE END 0 */

        2) 然后定义空闲中断处理函数和DMA发送函数 ,串口的Idle中断函数名需要申明到uart.h文件里,方便中断文件函数里调用

    /* USER CODE BEGIN 1 */
    #ifdef __GNUC__
     
      /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
     
      #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    	
    PUTCHAR_PROTOTYPE
    {
    	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    	return ch;
    }
     
    //DMA发送函数
    void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
    {
    	if(Length == 0) return;
    	while(xtUart1.Send == USART_DMA_SENDING){}
    	xtUart1.Send = USART_DMA_SENDING;
    	HAL_UART_Transmit_DMA(&huart1, pdata, Length);
    }
    
    void Usart3SendData_DMA(uint8_t *pdata, uint16_t Length)
    {
    	if(Length == 0 || xtUart3.Send == USART_DMA_SENDING) 
    		return;
    	while(xtUart3.Send == USART_DMA_SENDING){}
    	TXMODE_485();
    	xtUart3.Send = USART_DMA_SENDING;
    	HAL_UART_Transmit_DMA(&huart3, pdata, Length);
    	
    	
    
    }
     
    //DMA发送完成中断回调函数
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
    {
    	 __HAL_DMA_DISABLE(huart->hdmatx);
    	
    	if(huart->Instance == huart1.Instance)	
    		xtUart1.Send = USART_DMA_SENDOVER;	
    	else if(huart->Instance == huart3.Instance)
    	{
    		xtUart3.Send = USART_DMA_SENDOVER;
    		RXMODE_485();
    		LED1ON();
    		
    	}
    }
     
    static void HAL_UART_DMAStopRX(UART_HandleTypeDef *huart)
    {
    	CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);	
    	HAL_DMA_Abort(huart->hdmarx);	
    	CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
    	/* At end of Rx process, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;
    }
    
    //串口接收空闲中断
    void UsartReceive_IDLE(UART_HandleTypeDef *huart)
    {
    	if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
    	{ 		
    //		HAL_UART_DMAStop(huart);
    
    		HAL_UART_DMAStopRX(huart);
    		__HAL_UART_CLEAR_IDLEFLAG(huart);
    		if(huart->Instance == huart1.Instance)
    		{			
    			xtUart1.RxLen =  RECEIVELEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);
    			xtUart1.Receive=1;
    			HAL_UART_Receive_DMA(&huart1,xtUart1.RxBuf,RECEIVELEN);
    
    		}
    		else if(huart->Instance == huart3.Instance)
    		{
    			xtUart3.RxLen =  RECEIVELEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);
    			xtUart3.Receive=1;
    			HAL_UART_Receive_DMA(&huart3,xtUart3.RxBuf,RECEIVELEN);
    		}
    	}
    	LED1OFF();
    }
    
    void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
    {
    //	__HAL_UART_GET_IT_SOURCE(huart,UART_IT_ERR);
    	
    	__HAL_UART_CLEAR_PEFLAG(huart);
    	
    	//UART_FLAG_ORE/UART_FLAG_NE/UART_FLAG_FE/UART_FLAG_PE
    //	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_ORE))
    //		__HAL_UART_CLEAR_OREFLAG(huart);
    //	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_NE))
    //		__HAL_UART_CLEAR_NEFLAG(huart);
    //	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_FE))
    //		__HAL_UART_CLEAR_FEFLAG(huart);
    //	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_PE))
    //		__HAL_UART_CLEAR_PEFLAG(huart);
    
    }
    
    /* USER CODE END 1 */

    4. 在中断文件里添加(当然,上面的空闲中断处理函数需要声明)(stm32fxxxit.c)

    void USART1_IRQHandler(void)
    {
      /* USER CODE BEGIN USART1_IRQn 0 */
    	UsartReceive_IDLE(&huart1);
      /* USER CODE END USART1_IRQn 0 */
      HAL_UART_IRQHandler(&huart1);
      /* USER CODE BEGIN USART1_IRQn 1 */
     
      /* USER CODE END USART1_IRQn 1 */
    }

    5. 主函数里,打开空闲中断,初始化DMA接收

    /* USER CODE BEGIN 2 */
    __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_TC);
    	__HAL_UART_CLEAR_FLAG(&huart3,UART_FLAG_TC);
    	
    	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
    	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
    	
    	HAL_UART_Receive_DMA(&huart1,xtUart1.RxBuf,RECEIVELEN);
    	HAL_UART_Receive_DMA(&huart3,xtUart3.RxBuf,RECEIVELEN);
      /* USER CODE END 2 */

    6. 然后就可以在while(1)里处理数据了

    /* Infinite loop */
      /* USER CODE BEGIN WHILE */
    	
      while (1)
      {
    		if(xtUart1.Receive)//如果产生了空闲中断
    		{
    			xtUart1.Receive=0;//清零标记
    			Usart1SendData_DMA(xtUart1.RxBuf,xtUart1.RxLen);//串口打印收到的数据。
    		}
      /* USER CODE END WHILE */
     
      /* USER CODE BEGIN 3 */
    		
      }
      /* USER CODE END 3 */

    修改的代码就到这里了。但是HAL库版本更迭,可能函数名会不同。

    展开全文
  • STM32CubeMX 串口空闲中断加DMA 实现不定长度收发数据

    万次阅读 多人点赞 2016-06-24 11:03:14
    STM32CubeMX串口空闲中断+DMA实现不定长度收发数据,实现过程


    这里教程是选择的STM32F407。STM32F103也测试过。

    1.首先选择串口,选择异步通信。


    2.添加DMA


    3.打开中断


    4.生成代码,生成代码选择了每个外设单独使用.c/.h


    5.我使用的是Keil5。打开工程,首先注释掉dma.c里的DMA接收中断,因为不需要DMA接收中断,DMA发送中断是需要的。(dma.c)

    void MX_DMA_Init(void) 
    {
      /* DMA controller clock enable */
      __HAL_RCC_DMA2_CLK_ENABLE();
    
      /* DMA interrupt init */
      /* DMA2_Stream0_IRQn interrupt configuration */
      HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 2, 0);
      HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
      /* DMA2_Stream2_IRQn interrupt configuration */
    //  HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
    //  HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
      /* DMA2_Stream7_IRQn interrupt configuration */
      HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 1, 0);
      HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
    
    }
    6.在usart.h文件里定义串口接收数据类型我是如下定义的(usart.h)

    /* USER CODE BEGIN Private defines */
    #define RECEIVELEN 1024
    #define USART_DMA_SENDING 1//发送未完成
    #define USART_DMA_SENDOVER 0//发送完成
    typedef struct
    {
    uint8_t receive_flag:1;//空闲接收标记
    uint8_t dmaSend_flag:1;//发送完成标记
    uint16_t rx_len;//接收长度
    uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存
    }USART_RECEIVETYPE;
     
    extern USART_RECEIVETYPE UsartType1;
    
    
    /* USER CODE END Private defines */
    7.然后在usart.c里需要添加空闲中断处理函数,以及DMA发送函数等。(usart.c)

    首先定义串口数据类型:

    /* USER CODE BEGIN 0 */
    
    USART_RECEIVETYPE UsartType1;
    
    /* USER CODE END 0 */

    然后定义空闲中断处理函数和DMA发送函数

    /* USER CODE BEGIN 1 */
    #ifdef __GNUC__
    
      /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    
      #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    	
    PUTCHAR_PROTOTYPE
    {
    	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    	return ch;
    }
    
    //DMA发送函数
    void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
    {
    	while(UsartType1.dmaSend_flag == USART_DMA_SENDING);
    	UsartType1.dmaSend_flag = USART_DMA_SENDING;
    	HAL_UART_Transmit_DMA(&huart1, pdata, Length);
    }
    
    //DMA发送完成中断回调函数
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
    {
    	 __HAL_DMA_DISABLE(huart->hdmatx);
    	UsartType1.dmaSend_flag = USART_DMA_SENDOVER;
    }
    
    //串口接收空闲中断
    void UsartReceive_IDLE(UART_HandleTypeDef *huart)
    {
    	uint32_t temp;
    
    	if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
    	{ 
    		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
    		HAL_UART_DMAStop(&huart1);
    		temp = huart1.hdmarx->Instance->NDTR;
    		UsartType1.rx_len =  RECEIVELEN - temp; 
    		UsartType1.receive_flag=1;
    		HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN);
    	}
    }
    
    /* USER CODE END 1 */

    </pre><p></p><pre>
    8.在中断文件里添加(当然,上面的空闲中断处理函数需要声明)(stm32f4xxit.c)
    void USART1_IRQHandler(void)
    {
      /* USER CODE BEGIN USART1_IRQn 0 */
    	UsartReceive_IDLE(&huart1);
      /* USER CODE END USART1_IRQn 0 */
      HAL_UART_IRQHandler(&huart1);
      /* USER CODE BEGIN USART1_IRQn 1 */
    
      /* USER CODE END USART1_IRQn 1 */
    }

    9.主函数里,打开空闲中断,初始化DMA接收

    /* USER CODE BEGIN 2 */
    	HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN);
    	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
      /* USER CODE END 2 */

    10.然后就可以在while(1)里处理数据了

    /* Infinite loop */
      /* USER CODE BEGIN WHILE */
    	
      while (1)
      {
    		if(UsartType1.receive_flag)//如果产生了空闲中断
    		{
    			UsartType1.receive_flag=0;//清零标记
    			Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//串口打印收到的数据。
    		}
      /* USER CODE END WHILE */
    
      /* USER CODE BEGIN 3 */
    		
      }
      /* USER CODE END 3 */



    展开全文
  • 想在ATSAM芯片上也实现类似的功能,查看了数据手册,ATSAM上的DMA叫做PDCA,串口没有空闲中断,但是有个更高级的接收超时中断,这两个配合就能实现串口不定长度接收了。 二、串口DMA接收 ...

    一、前言

    DMA是单片机里的好东西,能把CPU从不断轮询和数据传输中解放出来,大大提高了系统性能。用过STM32就知道串口接收可以利用DMA配合空闲中断实现不定长度接收,数据通过DMA自动传输到缓存中,不用收到一个字节中断一次,十分方面。

    想在ATSAM芯片上实现类似的功能,查看了数据手册,ATSAM上的DMA叫做PDCA,串口没有空闲中断,但是有个更好的接收超时中断,这两个配合就能实现串口不定长度接收了。

    用到的工具如下:

    IDE:AtmelStudio 7

    芯片:ATSAM4LS2C (M4内核,主频48M)

    ASF版本:3.47

    仿真器:JLink v9

     

    二、串口DMA接收

     

    2.1 ASF库用到的模块如下

    在conf_uart_seral.h里定义好串口的配置

    #define USART_SERIAL                 USART0		//串口0
    #define USART_SERIAL_BAUDRATE        9600		//波特率
    #define USART_SERIAL_CHAR_LENGTH     US_MR_CHRL_8_BIT	//数据位8位
    #define USART_SERIAL_PARITY          US_MR_PAR_NO	//无奇偶校验
    #define USART_SERIAL_STOP_BIT        US_MR_NBSTOP_1_BIT	//1位停止位
    
    #define USART_SERIAL_RX_PIN           PIN_PA11A_USART0_RXD//接收引脚
    #define USART_SERIAL_RX_MUX           MUX_PA11A_USART0_RXD//复用Usart0接收
    
    #define USART_SERIAL_TX_PIN           PIN_PA12A_USART0_TXD//发送引脚
    #define USART_SERIAL_TX_MUX           MUX_PA12A_USART0_TXD//复用Usart0发送
    
    #define RX_BUFFER_SIZE		128	    //接收缓存长度
    #define PDCA_PID_USART_RX   0		//串口0接收 PID
    #define PDCA_RX_CHANNEL		0		//串口0接收 PDCA通道0
    
    #define TX_BUFFER_SIZE		128		//发送缓存长度
    #define PDCA_PID_USART_TX   18		//串口0发送 PID
    #define PDCA_TX_CHANNEL		1		//串口0发送 PDCA通道1

    2.2串口初始化

    void Usart0_init()
    {
    		//配置TX端口模式
    		ioport_set_pin_mode(USART_SERIAL_TX_PIN, USART_SERIAL_TX_MUX);
    		ioport_disable_pin( USART_SERIAL_TX_PIN);//关闭普通io功能
    		//配置RX端口模式
    		ioport_set_pin_mode( USART_SERIAL_RX_PIN,  USART_SERIAL_RX_MUX);
    		ioport_disable_pin( USART_SERIAL_RX_PIN);
    		
    		//串口配置选项
    		usart_serial_options_t usart_options = {
    			.baudrate = USART_SERIAL_BAUDRATE,		//波特率
    			.charlength = USART_SERIAL_CHAR_LENGTH,	//数据位宽
    			.paritytype = USART_SERIAL_PARITY,		//奇偶校验
    		.stopbits = USART_SERIAL_STOP_BIT};			//停止位
    		
    		// 初始化串口,重定向printf
    		stdio_serial_init(USART_SERIAL, &usart_options);
    		
    			usart_disable_writeprotect(USART_SERIAL);//关闭写保护
    			//超时时间10个时钟周期
    			usart_set_rx_timeout(USART_SERIAL,10);
    			usart_enable_writeprotect(USART_SERIAL);//使能写保护
    			usart_start_rx_timeout(USART_SERIAL);//清除接收超时标志,重新开始等待超时	
    			
    		//串口0总中断 抢占优先级0,响应优先级0
    		NVIC_SetPriority(USART0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
    		//使能USART0总中断
    		NVIC_EnableIRQ(USART0_IRQn);
    		//使能串口0接收超时中断
    		usart_enable_interrupt(USART_SERIAL, US_IER_TIMEOUT);
    		//使能发送
    		usart_enable_tx(USART_SERIAL);
    		//使能接收
    		usart_enable_rx(USART_SERIAL);
    }

    想利用DMA实现空闲中断,关键就在   usart_set_rx_timeout(USART_SERIAL,10);设置接收超时时间,根据数据手册里的描述,当收到一个字符后,开始超时计数,如果达到设定的超时时间还没有收到下一个字符,CSR.TIMEOUT就被置1,触发超时中断。

    像STM32空闲中断是判断超过一个字节的时间没有再收到字符,就认为总线空闲了,同理这里设置10个时钟周期,因为1个字节有10位,起始位+8位数据+停止位,效果就和总线空闲相同了,另外要注意Receiver Time-out Register是写保护的,要先调用usart_disable_writeprotect关闭写保护。

    2.3PDCA(DMA)接收初始化

    //接收缓存
    U8 RxBuffer[RX_BUFFER_SIZE];
    
    void Pdca_Rx_init(void)
    {
    	//pdca通道配置
    	pdca_channel_config_t pdca_rx_configs = {
    		.addr   = (void *)RxBuffer,     //内存地址
    		.pid    = PDCA_PID_USART_RX,    //外设ID
    		.size   = RX_BUFFER_SIZE,      //长度
    		.r_addr = (void *)0,		   //重装载内存地址
    		.r_size = 0,                  //重装载长度
    		.etrig  = false,              //外设事件触发
    		.ring   = false,              //环型buffer
    		.transfer_size = PDCA_MR_SIZE_BYTE //数据宽度
    	};
    
    	//使能pdca模块
    	pdca_enable(PDCA);
    
    	//初始化接收通道0
    	pdca_channel_set_config(PDCA_RX_CHANNEL, &pdca_rx_configs);
    	//设置中断回调函数
    	//pdca_channel_set_callback(PDCA_RX_CHANNEL, pdca_tranfer_done, PDCA_0_IRQn,1, PDCA_IER_TRC);
    	//使能接收通道0
    	pdca_channel_enable(PDCA_RX_CHANNEL);
    	
    }

      pdca_channel_config_t 配置选项简单介绍一下:

    (1)addr是数据地址,这里指向缓存RxBuffer

    (2).pid是外设的ID,这个要去翻数据手册,可以看到USART0 RX的ID是0,而USART0 TX的ID是18

    (3).size是长度,我们这里设置RxBuffer的大小。

    (4)r_addr是重装载的地址,这里没用到

    (5).r_size是重装载的长度,这里没用到

    (6).etrig是否外设事件触发,外设事件是PEVC,这里没用到。

    (7).ring是环形buffer,可以和r_addr、r_size配合使用,类似于STM32 DMA的循环模式,这里没用到。

    (8).transfer_size是数据宽度,有1个字节PDCA_MR_SIZE_BYTE;半字长(2字节)PDCA_MR_SIZE_HALF_WORD和一个字长(4字节)PDCA_MR_SIZE_WORD。

     

    2.4接收处理

    等到接收一帧完整的数据后才会触发中断,需要调用usart_start_rx_timeout清除超时标志,重新开始等待超时;同时重新装载一下DMA的长度。然后就可以愉快的处理数据了。

    //串口0中断函数
    void USART0_Handler(void)
    {
    	U32 u32_uart_status = usart_get_status(USART_SERIAL);
    	
    	/*-------------------------     接收中断处理	------------------------------*/	
    	if (u32_uart_status & US_CSR_TIMEOUT)//判断接收超时
    	{
    		usart_start_rx_timeout(USART_SERIAL);//清除接收超时标志,重新开始等待超时	
    		
    		pdca_channel_disable(PDCA_RX_CHANNEL);
    		U16 len = RX_BUFFER_SIZE - pdca_channel_read_load_size(PDCA_RX_CHANNEL);//计算接收到的数据帧长度
    		pdca_channel_write_load(PDCA_RX_CHANNEL,RxBuffer,RX_BUFFER_SIZE);//重新装载DMA长度
    		pdca_channel_enable(PDCA_RX_CHANNEL);
    		
    		printf("接收完成\r\n");
    		printf("长度:%d\r\n",len);
    		printf("数据:");
    		for(U16 i=0;i<len;i++)
    		{
    			usart_putchar(USART_SERIAL,RxBuffer[i]);
    		}
    		printf("\r\n\r\n");
    	}
    }

    5.效果

     

    三、串口DMA发送

    3.1 PDCA(DMA)发送初始化

    注意先不使能PDCA通道,因为一使能就开始发送了。

    
    void Pdca_Tx_init(void)
    {
    	//pdca通道配置
    	pdca_channel_config_t pdca_tx_configs = {
    		.addr   = (void *)TxBuffer,     //内存地址
    		.pid    = PDCA_PID_USART_TX,    //外设ID
    		.size   = TX_BUFFER_SIZE,      //长度
    		.r_addr = (void *)0,		   //重装载内存地址
    		.r_size = 0,                  //重装载长度
    		.etrig  = false,              //外设事件触发
    		.ring   = false,              //环型buffer
    		.transfer_size = PDCA_MR_SIZE_BYTE //数据宽度
    	};
    
    	//使能pdca模块
    	pdca_enable(PDCA);
    
    	//初始化接收通道1
    	pdca_channel_set_config(PDCA_TX_CHANNEL, &pdca_tx_configs);
    	//设置中断回调函数
    	//pdca_channel_set_callback(PDCA_RX_CHANNEL, pdca_tranfer_done, PDCA_0_IRQn,1, PDCA_IER_TRC);
    	//先不使能
    	pdca_channel_disable(PDCA_TX_CHANNEL);
    	
    }

     

    3.2发送函数

    这里简单发送0x00到0x07共8个字节,最后一使能就开始发送

    void Usart0_Sned_Frame()
    {
    	for(U8 i=0;i<8;i++)
    	{
    		TxBuffer[i]=i;
    	}
    		pdca_channel_disable(PDCA_TX_CHANNEL);
    		pdca_channel_write_load(PDCA_TX_CHANNEL,TxBuffer,8);//装载8个字节
    		pdca_channel_enable(PDCA_TX_CHANNEL);//使能发送
    }
    

    主函数里循环发送: 

    #define NVIC_PriorityGroup_2         ((uint32_t)0x5) /*!< 2 bits for pre-emption priority   2 bits for subpriority */
    int main (void)
    {
    	/* Insert system clock initialization code here (sysclk_init()). */
    	sysclk_init();
    	board_init();
    	delay_init();
    	NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);	//中断分组2 2位抢占,2位响应
    	
    	/* Insert application code here, after the board has been initialized. */	
    	Usart0_init();
    	Pdca_Rx_init();
    	Pdca_Tx_init();
    	
    	while(1)
    	{
    		Usart0_Sned_Frame();
    		delay_ms(500);
    	}
    }

    3.3效果

    展开全文
  • 1.首先选择串口,选择异步通信。 2.添加DMA 3.打开中断 ...4.生成代码,生成代码选择了每个外设单独使用.c/.h ...6.在usart.h文件里定义串口接收数据类型我是如下定义的(usart.h) /* USER CODE BEGIN Priva...
  • STM32在使用操作系统时频繁进出临界区会导致操作系统状态混乱从而造成不知道会发生什么的后果,写程序是不...所以通过定时器、串口中断为操作系统开发串口功能,以免在使用操作系统时对使用AT指令的串口外设造成影响!
  • 使用STM32cube创建的stm32f407的工程,使用UART1串口和DMA 串口空闲中断实现串口收发不定长度数据,节省CPU资源。
  • STM32 实现使用Command自动进入USB...STM32虚拟COM端口驱动程序 在App端需要用到USB虚拟串口来发送进入DFU的指令 编译工具可以使用IAR或KEIL,依照个人习惯,本文使用KEIL 5 DFU代码生成: STM32CubeMX部分: 打开STM32C
  • 上一次我们通过HAL库的串口中断回调函数,基本能够实现简单的不定长度读写收发的功能。这一次用DMA来实现,先了解一下DMA。DMA直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须...
  • 1基础知识点DMADMA(Direct Memory Access...STM32F4的DMA通道选择如下:接下来的程序思路如下:2编程要点2.1DMA发送2.1.1串口DMA发送配置由于是发送不定长的数据,先不需要配置发送的长度,在每次的发送时,再配置。...
  • 上一次我们通过HAL库的串口中断回调函数,基本能够实现简单的不定长度读写收发的功能。这一次用DMA来实现,先了解一下DMA。 DMA直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。...
  • 基础知识点 DMA DMA(Direct Memory Access),即直接内存存储,在...由于是发送不定长的数据,先不需要配置发送的长度,在每次的发送时,再配置。 //======================================= //串口DMA发送配置 //====
  • 一、硬件串口引脚定义 PE10-->USART0-TX PE11-->USART0-RX 上面表格中,红色方框画出的内容用程序实现如下,在串口初始化时... ~(_USART_ROUTELOC0_TXLOC_MASK| _USART_ROUTELOC0_RXLOC_MASK))//串口收发
  • 解决定长度串口收发多字节的一个缺陷。用代码来讲解,在原有串口接收机基础上,增加一个超时判断,利用一个定时器。[灵光一闪]串口中断与计时器中断首先你规划一个接收数组,设置它的合理最大长度,这里是rec_buf[10...
  • 1 基础知识点DMADMA(Direct Memory ...STM32F4的DMA通道选择如下:接下来的程序思路如下:2 编程要点2.1 DMA发送2.1.1 串口DMA发送配置由于是发送不定长的数据,先不需要配置发送的长度,在每次的发送时,再配置。/...
  • 1 基础知识点DMADMA(Direct Memory ...STM32F4的DMA通道选择如下:接下来的程序思路如下:2 编程要点2.1 DMA发送2.1.1 串口DMA发送配置由于是发送不定长的数据,先不需要配置发送的长度,在每次的发送时,再配置。/...

空空如也

空空如也

1 2 3
收藏数 53
精华内容 21
关键字:

串口不定长度收发数据