精华内容
下载资源
问答
  • <p>//串口引脚配置 static void App_PortInit(void) {<!-- -->  stc_gpio_cfg_t stcGpioCfg;  DDL_ZERO_STRUCT(stcGpioCfg);  Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //使...
  • 我做这个串口数据接收 dma+空闲中断 加fifo 实现串口的高效收发 ,主要是串口接收...1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的...

    我做这个串口数据接收 dma+空闲中断 加fifo  实现串口的高效收发  ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式  速度快和效率高,占用cpu的时间短

    对比了其他几种方式

    1:采用串口中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 频繁进中断,占用中断,特别是对时间和时序要求比较严格的时候 串口频繁进入中断导致其他中断时序有影响

     

    2:采用DMA方式接收数据,接收的数据长度必须是固定的  对于接收数据长度不固定就不怎么好弄了,特别像gprs通信,接收长度不固定,这些都是困扰我

     

    3:采用dma方式接收数据+定时器超时中断,这样来确定一帧数据完成,需要开关定时器,操作比较复杂,超时时间还不太好设置,stm32f1和f4 没有超时中断还只能采用定时器或者把rxd引脚接到stm32定时器触发引脚上来实现超时, 

    4:stm32串口dma方式接收数据+空闲中断或者超时中断+fifo 这种分内事来实现不定长的数据和高效的串口数据接收   效率比其他的方式要快,消耗cpu的时间比较少,这样应用可以做数据超时

    所以我采用了stm32串口dma方式接收数据+空闲中断或者超时中断+fifo方式来实现

    像stm32f103和stm32f407芯片没有时间超时中断  需要定时器来做超时 比较麻烦,nxp的部分芯片串口有超时中断,atmel的部分芯片串口有超时中断,stm32的h7和f7系列才有超时中断

    要实现stm32串口dma方式接收数据+空闲中断或者超时中断+fifo方式来实现

    第一步 stm32的串口dma配置 ,串口的初始化,还有串口的空闲中断

    1,通过stm32的cubemx软件来生成串口+dma配置的初始化

    cubemx生成代码都是HAL库的模式,我就以stm32f4的HAL库的方式实现

    UART_HandleTypeDef   huart1={0}; 
    
    huart1.Instance = USART1;
      huart1.Init.BaudRate = 115200;
      huart1.Init.WordLength = UART_WORDLENGTH_8B;
      huart1.Init.StopBits = UART_STOPBITS_1;
      huart1.Init.Parity = UART_PARITY_NONE;
      huart1.Init.Mode = UART_MODE_TX_RX;
      huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
      huart1.Init.OverSampling = UART_OVERSAMPLING_16;
      if (HAL_UART_Init(&huart1) != HAL_OK)
      {
      }

    dma的配置可以通过cubemx软件串口+dma方式来生成代码

      /* USER CODE BEGIN USART1_MspInit 0 */
    
      /* USER CODE END USART1_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_USART1_CLK_ENABLE();
      
        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**USART1 GPIO Configuration    
        PA9     ------> USART1_TX
        PA10     ------> USART1_RX 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        /* USART1 DMA Init */
        /* USART1_RX Init */
        hdma_usart1_rx.Instance = DMA2_Stream2;
        hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
        hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
        hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
        hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);
    
        /* USART1 interrupt Init */
        HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
        HAL_NVIC_EnableIRQ(USART1_IRQn);

    2,stm32生成的代码是不支持空闲中断的,需要自己增加空闲中断和空闲中断的处理

        __HAL_UART_ENABLE_IT(uart->h, UART_IT_IDLE);//使能空闲中断HAL代码
        __HAL_UART_CLEAR_IDLEFLAG(uart->h);//使能空闲中断HAL代码

    串口中断的处理

    void USART1_IRQHandler(void)
    { 
    
     HAL_UART_IRQHandler(&huart1); 
    
    if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) ? SET : RESET) == SET)
    
    {
    
    //串口空闲中断数据处理 
    
    }
    
    }

    第二步 了解环形fifo的buff特点,通过分析环形buff的特点,其实串口的dma接收 dma模式设置循环模式 就是环形buff

    环形buff的说明讲网站:https://blog.csdn.net/jiejiemcu/article/details/80563422

    转载环形fifo说明出处:    STM32进阶之串口环形缓冲区实现

    以下内容是引用了原文的内容 

    转载原文链接:https://blog.csdn.net/jiejiemcu/article/details/80563422 

     

    实现的原理:初始化的时候,列队头与列队尾都指向0,当有数据存储的时候,数据存储在‘0’的地址空间,列队尾指向下一个可以存储数据的地方‘1’,再有数据来的时候,存储数据到地址‘1’,然后队列尾指向下一个地址‘2’。当数据要进行处理的时候,肯定是先处理‘0’空间的数据,也就是列队头的数据,处理完了数据,‘0’地址空间的数据进行释放掉,列队头指向下一个可以处理数据的地址‘1’。从而实现整个环形缓冲区的数据读写。

      看图,队列头就是指向已经存储的数据,并且这个数据是待处理的。下一个CPU处理的数据就是1;而队列尾则指向可以进行写数据的地址。当1处理了,就会把1释放掉。并且把队列头指向2。当写入了一个数据6,那么队列尾的指针就会指向下一个可以写的地址。

    如果你懂了环形队列,那就跟着一步步用代码实现吧:

    是不是跟dma的circular的模式跟这个环形队列非常相似呢

    第3步就开始写串口dma+空闲中断或超时中断+fifo的代码啦

    重要的代码 串口dma的fifo的数据长度的处理

    //假设dma的接收缓存大小为512       环形buff的位置为 500    
    //接收到数据长度为48 这个时候的环形buff的位置为548                        
    // 548-500=48      但是dma环形buff的特性,
    //dma实际的长度为36 这个时候如何计算长度            
    // 这个算法如512-500 12      548-512       36
    rev_dma_lens =  DMA接收的数据长度
    //buff_index-----串口接收和app的buff的序列号
    //环形数组
    if (puart->ndtr_last != rev_dma_lens) //上次与这次不同,表示有新数据
    {
    
            start_addr = start_addr + len;//环形数据地址偏移量
            //
            if (start_addr >= 512)
            {
                puart->start_addr = start_addr - 512;
            }
            //
            if (rev_dma_lens > ndtr_last)
            {
                len = rev_dma_lens - ndtr_last; //接收数据长度=上次长度-这次长度
            }
            else
            {
                len =  512 - ndtr_last + rev_dma_lens; //环形数据到头后,总数-这次剩余+上次剩余
            }
            //
    
           ndtr_last = rev_dma_lens;
    
    }

    但是这种方式会2个问题:

    1,串口一直有数据接收 不产生空闲中断 这个时候会有问题  解决的方法采用超时方法来解决  或者采用一个一个的数据处理方式采用每来一个数据产生一个中断方式

    2, 一个数据包超过dma的最大buff长度  已经越界 导致了部分数据被覆盖啦   解决方法就是把DMA的接收buff定义更大一点 最好定义为2倍的一个最大数据包 考虑资源也可以考虑1.5或1倍的一个最大数据包 或者采用一个一个的数据处理方式  采用每来一个数据产生一个中断方式

    展开全文
  • 问题:程序烧芯片后,第一次可以正常实现,但后续就无法再进入中断了。用硬件仿真模式观察发现第一次发完了数已经跳出中断回到main函数,再发数就不再进入中断了。 解决:各个功能都正常,但进入中断后“清除中断...

    环境:使用STR912FAW44芯片,用MAX487将UART转成485信号与上位机联接。欲实现功能是:当从串口调试助手发一个数,进入串口中断后,返回1~10十个数。

    问题:程序烧进芯片后,第一次可以正常实现,但后续就无法再进入中断了。用硬件仿真模式观察发现第一次发完了数已经跳出中断回到main函数,再发数就不再进入中断了。

    解决:各个功能都正常,但进入中断后“清除中断标志位”,发送完成标志位等待也都正常配置。最终是试验了将串口助手发出的数给从中断里发回来,发现正常,但屏蔽发原数的语句后,在中断里发送连续1~10个数,只能实现一次。通过试验,发现必须加入语句读取串口助手的数据,程序成功。

    STR912的库函数里的清除中断标志的语句是个虚假的语句,并没有真正清除中断标志,只有读出串口助手发送的数据,才能真的清除中断标志,第n次发送数据都可以正常回复数据。

     

    展开全文
  • .NET串口通信中将`0X1A`当做EOF处理,.NET接收到EOF会触发一次接收中断,此时事件形参`SerialDataReceivedEventArgs`值为枚举 `Eof`,其他为`Chars`。 参考: stackoverflow、MSDN ## 测试 .NET编程,中断方式接收...

    .NET串口通信中将`0X1A`当做EOF处理,.NET接收到EOF会触发一次接收中断,此时事件形参`SerialDataReceivedEventArgs`值为枚举 `Eof`,其他为`Chars`。

    参考: stackoverflow 、MSDN

    ## 测试

    .NET编程,中断方式接收,发送一串数据后等待回应,打印进中断次数;另外一端用串口助手来实现。通信用虚拟串口。

     [TestClass]
        public class EofByteSerialTest
        {
            private int _count = 0;
            private byte[] _receivedDataPacket;
            SerialPort _serialPort = new SerialPort()
            {
                PortName = "COM5",
                BaudRate = 115200,
                DataBits = 8,
                StopBits = StopBits.One,
                Parity = Parity.None
            };
            [TestMethod]
            public void TestMethod1()
            {
                _serialPort.Open();
                byte[] sendedData = { 0x01, 0x02, 0x03, 0x1a, 0x2a, 0x1a };
                _serialPort.DataReceived += ComReceive;
                _serialPort.Write(sendedData, 0, sendedData.Length);
                Thread.Sleep(10000);
                //foreach (byte b in _receivedDataPacket)
                //{
                //    Console.WriteLine(b);
                //}
                Console.WriteLine(_count);
            }
            private void ComReceive(object sender, SerialDataReceivedEventArgs e)
            {
                //if (e.EventType == SerialData.Eof)
                //{
                //    //特殊处理
                //}
                _count++;
                _receivedDataPacket = new byte[_serialPort.BytesToRead];
                _serialPort.Read(_receivedDataPacket, 0, _receivedDataPacket.Length);
                // 协议解析...
            }
        }

     串口助手数据:

     

    测试结果:

     

     

    转载于:https://www.cnblogs.com/pangkang/p/6114293.html

    展开全文
  • 1.前言 DMA:直接存储器访问,优点是有DMA总线进行数据接收,...因为客户要求增加15个汉字,协议修改了,一次传输300+字节,接收中断和数据处理函数耗时较长,可能收到第一帧正在处理时候第二帧就发出来了,在处理.

    1. 前言

    DMA:直接存储器访问,优点是有DMA总线进行数据接收,不会占用CPU资源。

    在我目前负责的项目,之前是使用串口接收中断来判断是否完成数据接收,项目的坑货前任最开始的方法是在串口接收中断里面清除标志,在微系统的数据处理里面对标志位++,判断如果两次没进到接收中断就认为接收数据完成。

    最开始一帧数据只有77个字节,数据量较少,处理和接收都比较快。

    因为客户要求增加15个汉字,协议修改了,一次传输300+字节,接收中断和数据处理函数耗时较长,可能收到第一帧正在处理时候第二帧就发出来了,在处理完之后再去接收第二帧收到的是不完整的数据。在这个坑上面我改了好多次时间问题,让上位机改变发送时间间隔,从最开始的100ms到后来的500ms,各种修改。我师父每次听到我说改上位机时间就骂我,让我用帧头帧尾判断。

    当前的协议只用了一个字节来告诉我一帧数据,一个十六进制的字节最大只能表示255,因此在我接受这一帧数据时候需要先进行多步判断计算,然后得出帧尾位置,再用帧尾来判断是否传输结束。在串口接收中断里面进行多步判断导致接收数据紊乱,因此只能否定这种接收方式。

    师傅建议我改成DMA接收方式,于是有了这篇文章。

    修改前中断如下:

     

    修改前处理如下(微系统,任务队列,定时运行此方法,因此该方法的运行时间受到其他方法运行时间的影响,所以此方法中的标志位不能快速++,也不能快速解锁串口接收,最终导致接收中断数据紊乱):

     

    2. DMA相关概念

    看到我这篇文章时候想必大家都看过一些DMA的简介,这里主要讲述DMA使用,因此不会讲太多DMA概念相关知识。

    STM32大容量产品有两个DMA通道,DMA1和DMA2,我这里用的是DMA1。

    DMA1有7个通道,可以映射到不同的外设,如ADC、I2C、SPI、TIM等。

    通过下图我们可以看到UART5没有DMA映射,而我的憨憨前任画板子时候把数据接收放在了UART5,因此需要飞线把串口1和串口5的位置换一下。

    (发出去的机器只能通过修改协议把数据长度用两个字节表示以减少计算量来减少串口中断延时,保证数据接收准确)

     

    通过下图我们可以看到串口1的接收映射到DMA1的通道5,因此后续代码针对DMA1的通道5进行讲述

    3. 串口初始化

    串口初始化和普通串口初始化一样,如果有同学对串口设置还有疑问的请翻到我前面的串口相关文章查阅。

    使用DMA时候初始化唯一和普通串口初始化不同的地方是要加上这一句,开启串口空闲中断

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断 ***非常重要***

     

    4. 串口中断

    代码如下

    void USART1_IRQHandler(void)                	//串口1中断服务程序
    {
    	u8 clearTag;
    //    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        if((USART_GetITStatus(USART1, USART_IT_IDLE) != RESET))
        {
    //        USART_ClearITPendingBit(USART1, USART_IT_IDLE);
    		clearTag=USART1->DR;//软件序列清除IDLE标志位
            MYDMA_Config();
        }
    }

     

    与接收中断一样,空闲中断也是读取空闲中断寄存器判断是否可以进入中断

     

    本章节增加了点新知识,之前清除中断标志位都是使用函数来清除。而实际上中断里面的标志位寄存器在读取之后就会被清除置零,因此随便声明一个变量,用这个变量去读一次相关的寄存器就可以了。

     

    清除完空闲标志位之后,初始化一下DMA配置,这样会让接收数组从0开始存,否则会继续从后面追加存储。这个地方应该可以直接通过寄存器来清零,但是我现在不会用就不写了,后面学会了在加上来。DMA初始化方法在后面。

    后面发现只需要重置传输数据量就可以了,也就是简单的重置一个寄存器,毕竟重置初始化方法比重置一个寄存器要消耗的资源多得多。更改后的程序作为优化写在后面。

    MYDMA_Config();

     

    5. DMA初始化

    代码如下

    #include "dma.h"
    #include "usart.h"
    
    
    
    u8 RX[306];
    
    void MYDMA_Config()
    {
    	DMA_Channel_TypeDef* DMA_CHx;
    	u16 DMA1_MEM_LEN;
        u32 cpar;
        u32 cmar;
    	
        DMA_CHx=DMA1_Channel5; //通道号
        cpar=(u32)&USART1->DR; //源地址
        cmar=(u32)&RX; //目的地址
    	DMA1_MEM_LEN=306;  //传输数据大小、同时也是DMA缓存大小
    	
        DMA_InitTypeDef DMA_InitStructure;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输
        DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为通道5
        
        DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
        DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从内存读取发送到外设
        DMA_InitStructure.DMA_BufferSize = DMA1_MEM_LEN;  //DMA通道的DMA缓存的大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在正常缓存模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //DMA通道 x拥有中优先级
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
        DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
    
        DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);
        DMA_Cmd(DMA_CHx, ENABLE);
        USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    
    }
    
    
    

    原谅我在前面写了一堆乱七八糟的定义,因为是直接用原子的代码,他的函数接口里面写了很多定义,需要从主函数传进来。

    我这里为了简化代码直接写死,同时为了让大家看清楚哪些是需要自己定义的变量,在前面写了很多的定义。

    接收数组全局变量放在外面主要是为了调试时候用。

    如下图,我们需要4个自定义的变量来初始化DMA,作用在后面的注释中写的很清楚了。

     

    需要注意的有这个,数据传输方向,这个是从外设到内存。因为我们是把数据从串口传到内存数组里面,因此用这个

    可以看到他的头文件里面定义了两种模式,还有一种是从内存到外设,这个可以用在从内存把数据放到存储芯片如24C02或者SD内存卡里面。

     

    还有个注意的地方是这个:

    外设地址寄存器不变,是因为串口中断一次只接收一个字节的数据,取出来上一个之后又会把下一个收到的数据放在这个里面,因此他的地址是不需要变化的,也就是地址寄存器不变。

    内存地址递增,因为内存地址给的是一个数组地址,数组里面连续存储的数据地址是递增变化的。如果使用地址不变,则会将数据一直放在数组的第一位。

     

    至于这几个设置,通用的设置应该都可以了

     

    这个是告诉DMA要传多少个数据,可以在中断里面得到这个数据判断本次传输多少个数据。

     

    这个是开启UART到DMA的映射,我之前程序运行DMA一直收不到数据就是没开这个的原因。

     

    6.  优化

    优化后中断代码如下

    u16 countNum;
    void USART1_IRQHandler(void)                	//串口1中断服务程序
    {
        u8 clearTag;
    //    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        if((USART_GetITStatus(USART1, USART_IT_IDLE) != RESET))
        {
    //      USART_ClearITPendingBit(USART1, USART_IT_IDLE);
    		countNum=DMA1_Channel5->CNDTR; //获取剩余应该传输的字节数
            clearTag=USART1->DR;//软件序列清除IDLE标志位
    		
            DMA_Cmd(DMA1_Channel5,DISABLE); //要关闭之后再设置,否则设置无法生效
            DMA_SetCurrDataCounter(DMA1_Channel5,306); //重设长度想当于重置接收数组下标为0
            DMA_Cmd(DMA1_Channel5,ENABLE);
        }
    }

     

    如图,使用上面的方法直接重置传输长度之后,间隔1ms发送数据,数组没有被覆盖,说明本方法想当于前面的重新初始化DMA方法。

     

    剩余数据量,如图,设置一次传输306字节,我们发了13个字节,发现13+291=304,和预计的不一样,仔细看发现串口助手自动在、发送的数据后面拼接了0D 0A,因此13+291+2=306正确。

     

    7. 全部代码

    main.c

    #include "stm32f10x.h"
    #include "delay.h"
    #include "led.h"
    #include "pwm.h"
    #include "usart.h"
    #include "dma.h"
    
    
    extern u8 UartLock;
    
    int main(void)
    {
    
        delay_init();
        ledInit();
        uart_init(115200);
        MYDMA_Config();
        u16 t;
        u16 len;
    
        while(1)
        {
            
               LED0=!LED0;//闪烁LED,提示系统正在运行.
               delay_ms(500);
            
        }
    
    }
    
    
    
    
    

    usart.c

    #ifndef __USART_H
    #define __USART_H
    #include "stdio.h"	
    #include "sys.h" 
    
    
    #define USART_REC_LEN  			200  	//定义最大接收字节数 200
    #define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
    	  	
    extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
    extern u16 USART_RX_STA;         		//接收状态标记	
    
    //如果想串口中断接收,请不要注释以下宏定义
    void uart_init(u32 bound);
    #endif
    
    
    

    usart.c

    #include "sys.h"
    #include "usart.h"
    #include "dma.h"
    
    u8 UartLock=0;
    
    #if 1
    #pragma import(__use_no_semihosting)
    //标准库需要的支持函数
    struct __FILE
    {
        int handle;
    
    };
    
    FILE __stdout;
    //定义_sys_exit()以避免使用半主机模式
    void _sys_exit(int x)
    {
        x = x;
    }
    //重定义fputc函数
    int fputc(int ch, FILE *f)
    {
        while((USART1->SR&0X40)==0)
        {
    
        }
        //循环发送,直到发送完毕
        USART1->DR = (u8) ch;
        return ch;
    }
    #endif
    
    
    
    #if EN_USART1_RX   //如果使能了接收
    //串口1中断服务程序
    //注意,读取USARTx->SR能避免莫名其妙的错误
    u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    //接收状态
    //bit15,	接收完成标志
    //bit14,	接收到0x0d
    //bit13~0,	接收到的有效字节数目
    u16 USART_RX_STA=0;       //接收状态标记
    
    void uart_init(u32 bound) {
        //GPIO端口设置
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
    
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
    
        //USART1_TX   GPIOA.9
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
        GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
    
        //USART1_RX	  GPIOA.10初始化
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
        GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
    
        //Usart1 NVIC 配置
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
        NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器
    
        //USART 初始化设置
        USART_InitStructure.USART_BaudRate = bound;//串口波特率
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
        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); //初始化串口1
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
        USART_Cmd(USART1, ENABLE);                    //使能串口1
    
        USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断 ***非常重要***
    }
    
    u16 countNum;
    void USART1_IRQHandler(void)                	//串口1中断服务程序
    {
        u8 clearTag;
    //    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        if((USART_GetITStatus(USART1, USART_IT_IDLE) != RESET))
        {
    //      USART_ClearITPendingBit(USART1, USART_IT_IDLE);
    		countNum=DMA1_Channel5->CNDTR; //获取剩余应该传输的字节数
            clearTag=USART1->DR;//软件序列清除IDLE标志位
    		
            DMA_Cmd(DMA1_Channel5,DISABLE); //要关闭之后再设置,否则设置无法生效
            DMA_SetCurrDataCounter(DMA1_Channel5,306); //重设长度想当于重置接收数组下标为0
            DMA_Cmd(DMA1_Channel5,ENABLE);
        }
    }
    #endif
    
    

    dma.h

    #ifndef __DMA_H
    #define	__DMA_H	   
    #include "sys.h"						    					    
    
    //void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);//配置DMA1_CHx
    
    void MYDMA_Config(void);
    		   
    #endif
    
    
    

    dma.c

    #include "dma.h"
    #include "usart.h"
    
    
    
    u8 RX[306];
    
    void MYDMA_Config()
    {
    	DMA_Channel_TypeDef* DMA_CHx;
    	u16 DMA1_MEM_LEN;
        u32 cpar;
        u32 cmar;
    	
        DMA_CHx=DMA1_Channel5; //通道号
        cpar=(u32)&USART1->DR; //源地址
        cmar=(u32)&RX; //目的地址
    	DMA1_MEM_LEN=306;  //传输数据大小、同时也是DMA缓存大小
    	
        DMA_InitTypeDef DMA_InitStructure;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输
        DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为通道5
        
        DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
        DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从内存读取发送到外设
        DMA_InitStructure.DMA_BufferSize = DMA1_MEM_LEN;  //DMA通道的DMA缓存的大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在正常缓存模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //DMA通道 x拥有中优先级
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
        DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道    
        USART1_Tx_DMA_Channel所标识的寄存器
    
    	DMA_Cmd(DMA_CHx, ENABLE);
        DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);
        USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    
    }
    
    
    

     

    以上。

    展开全文
  • ✅ 解决:在每次发送的后一句加一句延时,延时太长只能进一次中断,延时太短发的数据还是错误。但五次发送时可以收到11 22 44 55。 继续改进,每句串口发送后加入获取发送完成的flag语句,(当TX发送完成后flag置1)...
  • 本文的目的就是提出一种解决方式,当接受的字符个数大于8个时,在一次中断处理函数中全部接收到这些字符串,而不用多次进入中断处理函数。   串口的控件初始化: <br />m_ComPort.SetCom
  • 我现在是让个板子一直发消息,另个板子产生接收中断,进入接收中断服务程序。但是一直不去。还请大家帮忙看看问题出在哪里?可能就是语惊醒梦中人啊。下面这部分是发送程序。应该没什么问题,在串口显示是...
  • //一次中断为277.76us } void int0() interrupt 0 { static uchar i; static bit flag; if(flag) { ES=0; if((time0)&&(time0>32)) i=0; IRdata[i]=time0; ...
  • 之前用串口的USART_IT_IDLE中断和USART_IT_RXNE(此中断每接收一个字节就会进一次中断),然后利用USART_IT_IDLE检测空闲接收完整的一帧数据,但是此方法需要频繁进入中断,影响CPU效率,所以利用USART_IT_IDLE+DMA...
  • 之前使能串口中断 调用HAL_UART_Receive_IT(); 为了之后其他串口使用相同函数发送CMD void UART_PortConfig(uint8_t num, UART_HandleTypeDef *h_uart) { UART_Handle_Array[num-1] = h_uart; /* Enable the ...
  • STM32F4串口DMA配置

    千次阅读 2017-03-22 21:01:36
    如果有一种方式,能够让串口收完一串数据,才进一次中断,那将是对写底层驱动的人来说,是极其好的一件事。经过查资料看手册,发现可以采用串口空闲中断和DMA接收来实现这个功能。具体更详细的说明后续补充,现只贴...
  • STM32 串口驱动,分层通信

    千次阅读 2018-03-02 10:21:04
    以前在使用串口的时候都是直接使用中断,每收发一个字节都要进一次中断,然后直接在中断进行封包,现在做了一个简单的分层设计,其实这个设计还是驱动设计,后期将逻辑层划分再细致一点,争取做到和linux的shell类似...
  • 串口接收数据有误,求指导!

    千次阅读 2018-11-19 17:53:09
    上位机给串口发送数据,单片机通过串口接收到的数据以什么结尾@TOC 串口接收到数据后对数据最后几位进行判断,总是不去判断函数是什么原因,串口接收... 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你...
  • 串口通讯中出现的错误(1)

    千次阅读 2018-04-17 22:11:00
    问题1: 在做串口接收接收中断时,有这么个现象,当接收到数据后,进入中断,如果再次发送的话,就不去中断了,并且程序阻塞住了?为什么呢 源码如下: 原因如下:在数据寄存器和数据总线之间有个移位...
  • 项目做到一半,碰到个尴尬问题:PWM使用的DMA通道与串口接收的DMA...每个字节都进一次hal库的接收处理函数,进行各种判断实在是不友好,因此考虑跳过hal的串口处理部分,直接判断uart_flag_rxne标志位与uart_flag_idl
  • 写一个程序,用到了ucos ii ,串口中断中接收数据(一包数据 8个字节 包含: 1byte包头 5byte数据 1byte校验和 1byte 包尾 ) ,数据由上位机每隔500ms发送一次,在串口中断中接收1byte数据放到数组Rev_dat[8]中并...
  • 使用nRF52810开发产品,一次不经意删除了工程目录\examples\ble_peripheral\ble_app_uart\pca10040e\s112\arm5_no_packs下的RTE文件夹,虽然重新编译会生成一个新的RTE文件夹,但是会导致串口一通信,程序就会卡死...
  • UART的FIFO功能

    2019-05-28 09:13:25
    经常听到UART的FIFO功能,但是从来没有真正使用过和认真思考过它的...如果没有FIFO或者说缓冲区的长度只有1字节,那么使用接收中断,就意味着每次收到一个字节的数据就要进一次中断,这样频繁进中断会占用CPU资源...
  • 、建立自己的平台类型 (1)解压文件 #tar jxvf u-boot-1.3.1.tar.bz2 (2)进入 U-Boot源码目录 #cd u-boot-1.3.1 (3)创建自己的开发板: #cd board #cp smdk2410 fs2410 –a #cd fs2410 #mv smdk2410...
  • c语言编写单片机技巧

    2009-04-19 12:15:17
    要实现单片机与电脑的连接,最简单的方式就是采用串口通讯,但需要加一颗RS-232芯片。 17. 在ARM编程中又应当如何? 答:就以嵌入式系统观念为例,一般嵌入式处理器可以分为三类:嵌入式微处理器、嵌入式微...
  • 定时器溢出一次就发送一个位的数据 REN=1;//串行接收允许位(要先设置sm0sm1再开串行允许) EA=1;//开总中断 ES=1;//开串行口中断 } void Uart1BYTE(uchar temp) { SBUF=temp; ...

空空如也

空空如也

1 2
收藏数 23
精华内容 9
关键字:

串口进一次中断