你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

请教DMA缓冲区大小修改

[复制链接]
feiante 提问时间:2014-12-12 15:25 /
我在用DMA进行串口数据发送和接收,如果不改变传输时缓冲区大小,比如一开始定义好缓冲区大小是20,然后接收到或者发送的数据大小就是20,而且数据是正确的。
现在想再接收到一串数据后,修改缓冲区大小的大小,比如收到的某20个数据后,要把缓冲区发送缓冲区大小改成6,接收缓冲区大小改成13,然后在收到另一组数据后,在改回20。我现在的做法是在接收完成中断中,关闭DMA,然后配置发送DMA_BufferSize = 6;  配置接收DMA_BufferSize = 13; 然后在使能DMA,结果就不能发送和接收数据。
应该如何修改缓冲区大小呢?
收藏 评论4 发布时间:2014-12-12 15:25

举报

4个回答
小贾-370388 回答时间:2014-12-12 16:34:24
好像是在运行中更改可能不好实现,串口通信是经常使用到的功能,在STM32中UART具有DMA功能,并且收发都可以使用DMA,使用DMA发送基本上大家不会遇到什么问题,因为发送的时候会告知DMA发送的数据长度,DMA按照发送的长度直接发送就OK了,但是使用DMA接收时候就不同了,因为有时候数据接收并不是每一次都是定长的,但是DMA只在接收数据长度和设定数据长度相同的时候才可以触发中断,告诉MCU数据接收完毕,针对这个问题,解决方法如下,有一点复杂,但是很管用。首先要了解一下串口数据传输格式,UART在传输一个字节的时候,首先拉低,传输起始位,然后在是LSB –MSB,最后是停止位,停止位是高电平。
2、        超时时间
搞过串口通信的都知道,如果串口有协议,一般都是有个超时时间的,超时时间是定义两个帧之间的间隔的,如果串口接收到一个字节后,在规定的超时时间内没有接收到其他数据,我们则认为前面接收的数据位一帧。
3、        定时器复位复位模式
STM32定时器功能比较强大,其中有一种模式为复位模式,
整体的思路是这样的,一开始设置好DMA接收,可以把缓冲区长度设置为帧最大长度,我们可以把RX连接到定时器的管脚输入端,并且一开始设置输入并且使能引脚下降沿中断,当帧的第一个字节发送时,因为起始位为低电平,空闲时UART为高电平,满足条件,进入中断,禁止中断,并且在中断中开启定时器,该定时器工作在复位模式,上升沿复位,并且设置好定时器输出比较值为超时时间,比如20ms,这样,在传输后面字节时,肯定会有高低电平出现,即便是传输的是0x00,0xFF,虽然UART数据区不变,但是都为1,或都为0,但是因为起始位为低电平,停止位是高电平,所以肯定会有上升沿,定时器会一直复位,输出定时器的计数器一直到达不了输出比较值,当一帧传输结束后,定时在最后一个字节复位后,由于没有数据继续到达,无法复位,则计数器就能计到输出比较值,这时发出中断,在定时器中断中可以计算出接收数据的长度,并且通知外部数据已经接收完毕。
4、        功能实现
实现的步骤:
1、硬件连接:UART的RX线在连接外部的同时,还需要连接到一个定时器的输入端TIMx_CHx,定时器可以为任意定时器,但是CHx,只能为CH1或CH2,具体的需要看STM32的定时器逻辑图,以STM32F101CB为例,我们暂定把UART1的RX在连接RS232的同时,还连接到TIM4_CH2。
2、软件设置
a) IO、中断设置:在把UART功能口设置好后,还需要设置TIM4_CH2为输入上拉,并且使能该引脚外部中断

/**
  * @brief  Configures the different GPIO ports.
  * @param  None
  * @retval : None
  */
void GPIO_Configuration(void)
{
/* Configure USART1_Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
   
/* Configure USART1_Tx as alternate push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* GPIOB.7 Configuration: TIM4 Channel2 as input floatinng */
GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
  * @brief  Configures the system EXIT.
  * @param  None
  * @retval None
  */
void EXTI_Configuration(void)
{
    EXTI_InitTypeDef   EXTI_InitStructure;
    /* Connect EXTI8 Line to PB.07 pin */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource7);
   
    /* Configure EXTI8 line */
    EXTI_InitStructure.EXTI_Line = EXTI_Line7;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
   
    /* Clears  EXTI line pending bits. */
    EXTI_ClearITPendingBit(EXTI_Line7);
}

/**
  * @brief  Configures NVIC and Vector Table base location.
  * @param  None
  * @retval : None
  */
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Configure the NVIC Preemption Priority Bits*/  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    /* Set the Vector Table base location at 0x08000000 */
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
  
   
    /* Enable the DMA1_Channel_Rx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
   
    /* Configure DMA1_Channel_Tx interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
   
    /* Configure TIM1 update interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
   
    /* Enable and set EXTI9_5 Interrupt to the lowest priority */
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
把DMA接收的数据缓冲区设置为你认为最大的帧长度,(如果最长不能确定,也可以随便指定一个长度,后面再讲怎么实现)。
b) 定时器设置
因为使用的是TIM4_CH2,所以需要配置TIM4,并且配置为复位模式,把超时时间定为20ms,为了方便TIM4时钟定输入为1KHZ
/*
* This function is called by timer_init() to perform the non-generic portion
* of the initialization of the timer module.
*/
void timer_init_non_generic(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    TIM_ICInitTypeDef  TIM_ICInitStructure;
   
    /* TIM4 configuration ----------------------------------------------------*/
    TIM_TimeBaseStructure.TIM_Period = 65535;
    TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/1000000 * 1000 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
   
    /* Output Compare  Mode configuration: Channel1 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
    TIM_OCInitStructure.TIM_Pulse = 20;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM4, &TIM_OCInitStructure);
   
    /* TIM4 Channel 2 Input Capture Configuration */
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0;
    TIM_ICInit(TIM4, &TIM_ICInitStructure);
   
    /* TIM4 Input trigger configuration: External Trigger connected to TI2 */
    TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);
   
    /* TIM4 configuration in slave reset mode  where the timer counter is
    re-initialied in response to rising edges on an input capture (TI2) */
    TIM_SelectSlaveMode(TIM4,  TIM_SlaveMode_Reset);
   
    /* TIM4 IT CC1 enable */
    TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
   
}
c)工作过程如下
在串口传输起始位的时候,首先产生外部中断,在外部中断中开启定时器,禁止外部中断,只要串口上一直有数据,定时器肯定会不停的复位,到达不了定时时间,当串口上没有数据的时候,到超时时间后,定时器产生中断,此时可以读出接收的数据长度,然后开启外部中断,进入下一个周期。



feiante 回答时间:2014-12-13 21:40:15
小贾-370388 发表于 2014-12-12 16:34
好像是在运行中更改可能不好实现,串口通信是经常使用到的功能,在STM32中UART具有DMA功能,并且收发都可以 ...

谢谢                       
奔跑小蜗牛 回答时间:2014-12-14 13:29:36
DMA的正常模式和循环模式,在循环模式下更改缓冲区大小,DMA会不能正常收发数据。正常模式下是单次收发,把你的数据收发做成单次收发模式,这样修改缓冲区就不会出问题吧。
奥丁泰尔 回答时间:2016-10-9 10:54:57
如果定义的dma缓冲区大小L1,小于接收的帧长度L时,如何处理呢?

所属标签

相似问题

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版