谁用过STM32 DMA2 可用于中断处理

STM32 DMA使用详解
单片机&嵌入式
单片机应用
嵌入式操作系统
学习工具&教程
学习和开发单片机的必备工具
(有问必答)
(带你轻松入门)
电子元件&电路模块
当前位置: >>
>> 浏览文章
STM32 DMA使用详解
DMA部分我用到的相对简单,当然,可能这是新东西,我暂时还用不到它的复杂功能吧。下面用问答的形式表达我的思路。
DMA有什么用?
&&&&&& 直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
有多少个DMA资源?
&&&&&& 有两个DMA控制器,DMA1有7个通道,DMA2有5个通道。
数据从什么地方送到什么地方?
&&&&&& 外设到SRAM(I2C/UART等获取数据并送入SRAM);
&&&&&& SRAM的两个区域之间;
&&&&&& 外设到外设(ADC读取数据后送到TIM1控制其产生不同的PWM占空比);
&&&&&& SRAM到外设(SRAM中预先保存的数据送入DAC产生各种波形);
&&&&&& &&还有一些目前还搞不清楚的。
DMA可以传递多少数据?
&&&&&& 传统的DMA的概念是用于大批量数据的传输,但是我理解,在STM32中,它的概念被扩展了,也许更多的时候快速是其应用的重点。数据可以从1~65535个。
直接存储器存取(Direct Memory Access,DMA)是计算机科学中的一种内存访问技术。它允许某些电脑内部的硬体子系统(电脑外设),可以独立地直接读写系统存储器,而不需绕道 CPU。在同等程度的CPU负担下,DMA是一种快速的数据传送方式。它允许不同速度的硬件装置来沟通,而不需要依于 CPU的大量中断请求。【摘自Wikipedia】
现在越来越多的单片机采用DMA技术,提供外设和存储器之间或者存储器之间的高速数据传输。当 CPU 初始化这个传输动作,传输动作本身是由&DMA 控制器&来实行和完成。STM32就有一个DMA控制器,它有7个通道,每个通道专门用来管理一个或多个外设对存储器访问的请求,还有一个仲裁器来协调各个DMA请求的优先权。
DMA 控制器和Cortex-M3核共享系统数据总线执行直接存储器数据传输。当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求可能会停止 CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。
在发生一个事件后,外设发送一个请求信号到DMA控制器。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问外设的时候,DMA控制器立即发送给外设一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果发生更多的请求时,外设可以启动下次处理。
总之,每个DMA传送由3个操作组成:
1. 从外设数据寄存器或者从DMA_CMARx寄存器指定地址的存储器单元执行加载操作。
2. 存数据到外设数据寄存器或者存数据到DMA_CMARx寄存器指定地址的存储器单元。
3. 执行一次DMA_CNDTRx寄存器的递减操作。该寄存器包含未完成的操作数目。
仲裁器根据通道请求的优先级来启动外设/存储器的访问。优先级分为两个等级:软件(4个等级:最高、高、中等、低)、硬件(有较低编号的通道比拥有较高编号的通道有较高的优先权)。
可以在DMA传输过半、传输完成和传输错误时产生中断。
STM32中DMA的不同中断(传输完成、半传输、传输完成)通过&线或&方式连接至NVIC,需要在中断例程中进行判断。
进行DMA配置前,不要忘了在RCC设置中使能DMA时钟。STM32的DMA控制器挂在AHB总线上。
DMA总共有7个通道,各个通道的DMA映射关系如下:
外设的事件连接至相应DMA通道,每个通道均可以通过软件触发实现存储器内部的DMA数据传输(M2M模式)
Tips:库2.0中函数RCC_AHBPeriphClockCmd的参数由&RCC_AHBPeriph_DMA&改成&RCC_AHBPeriph_DMA1&(如果是DMA1控制器的话)。
DMA的传输标志位(CHTIFx、CTCIFx、CGIFx)由硬件设置为&1&,但需要软件清零,在中断服务程序中清除。当CGIFx(全局中断标志位)清零后,CHTIFx 和 CTCIFx均清零。
过程:怎样启用DMA?首先,众所周知的是初始化,任何设备启用前都要对其进行初始化,要对模块初始化,还要先了解该模块相应的结构及其函数,以便正确的设置;由于DMA较为复杂,我就只谈谈DMA的基本结构和和常用函数,这些都是ST公司提供在库函数中的。
1、 下面代码是一个标准DMA设置,当然实际应用中可根据实际情况进行裁减:
DMA_DeInit(DMA_Channel1);
上面这句是给DMA配置通道,根据ST提供的资料,STM3210Fx中DMA包含7个通道(CH1~CH7),也就是说可以为外设或memory提供7座&桥梁&(请允许我使用桥梁一词,我觉得更容易理解,哈哈,别&拍砖&呀!);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_A
上面语句中的DMA_InitStructure是一个DMA结构体,在库中有声明了,当然使用时就要先定义 了;DMA_PeripheralBaseAddr是该结构体中一个数据成员,给DMA一个起始地址,好比是一个buffer起始地址,数据流程是:外设 寄存器& DMA_PeripheralBaseAdd&memory中变量空间(或flash中数据空间等),ADC1_DR_Address是我定义的一个地址 变量;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedV
上面这句很显然是DMA要连接在Memory中变量的地址,ADC_ConvertedValue是我自己在memory中定义的一个变量;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
上面的这句是设置DMA的传输方向,就如前面我所说的,DMA可以双向传输,也可以单向传输,这里设置的是单向传输,如果需要双向传输:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。
DMA_InitStructure.DMA_BufferSize = 2;
上面的这句是设置DMA在传输时缓冲区的长度,前面有定义过了buffer的起始地址:ADC1_DR_Address ,为了安全性和可靠性,一般需要给buffer定义一个储存片区,这个参数的单位有三种类型:Byte、HalfWord、word,我设置的2个 half-word(见下面的设置);32位的MCU中1个half-word占16 bits。
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_D
上面的这句是设置DMA的外设递增模式,如果DMA选用的通道(CHx)有多个外设连接,需要使用外设递增模式:DMA_PeripheralInc_E我的例子里DMA只与ADC1建立了联系,所以选用DMA_PeripheralInc_Disable
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_E
上面的这句是设置DMA的内存递增模式,DMA访问多个内存参数时,需要使用DMA_MemoryInc_Enable,当DMA只访问一个内存参数时,可设置成:DMA_MemoryInc_Disable。
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfW
上面的这句是设置DMA在访问时每次操作的数据长度。有三种数据长度类型,前面已经讲过了,这里不在叙述。
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfW
与上面雷同。在此不再说明。
DMA_InitStructure.DMA_Mode = DMA_Mode_C
上面的这句是设置DMA的传输模式:连续不断的循环模式,若只想访问一次后就不要访问了(或按指令操作来反问,也就是想要它访问的时候就访问,不要它访问的时候就停止),可以设置成通用模式:DMA_Mode_Normal
DMA_InitStructure.DMA_Priority = DMA_Priority_H
上面的这句是设置DMA的优先级别:可以分为4级:VeryHigh,High,Medium,Low.
DMA_InitStructure.DMA_M2M = DMA_M2M_D
上面的这句是设置DMA的2个memory中的变量互相访问的
DMA_Init(DMA_Channel1,&DMA_InitStructure);
前面那些都是对DMA结构体成员的设置,在次再统一对DMA整个模块做一次初始化,使得DMA各成员与上面的参数一致。
DMA_Cmd(DMA_Channel1,ENABLE);
哈哈哈!这一句我想我就不罗嗦了,大家一看就明白。
至此,整个DMA总算设置好了,但是,DMA通道又是怎样与外设联系在一起的呢?哈哈,这也是我当初最想知道的一个事情,别急!容我想喝口茶~~~~~~哈哈哈!
要使DMA与外设建立有效连接,这不是DMA自身的事情,是各个外设的事情,每个外设都有 一个xxx_DMACmd(XXXx,Enable )函数,如果使DMA与ADC建立有效联系,就使用ADC_DMACmd(ADC1,Enable); (这里我启用了ADC中的ADC1模块)。
一个简单的例子 transfer& a word data buffer from FLASH memory to embedded SRAM memory.
在V3.1.2库的位置
STM32F10x_StdPeriph_Lib_V3.1.2\Project\STM32F10x_StdPeriph_Examples\DMA\FLASH_RAM
DMA_DeInit(DMA1_Channel6);
& //peripheral base address
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_B
& //memory base address&&&
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_B
& //数据传输方向&&& Peripheral is source &&& &&& &&& &&
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//缓冲区大小 Number of data to be transferred (0 up to 65535).数据传输数目&&& &
DMA_InitStructure.DMA_BufferSize = BufferS
&& // the Peripheral address register is incremented&&& &&&
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_E
& //the memory address register is incremented
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_E
//the Peripheral data width&&& &&&
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_W&
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_W
DMA_InitStructure.DMA_Mode = DMA_Mode_N
DMA_InitStructure.DMA_Priority = DMA_Priority_H
//the DMAy Channelx will be used in memory-to-memory transfer
//DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。
DMA_InitStructure.DMA_M2M = DMA_M2M_E&&&
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel6, ENABLE);
=======================================================================
外设的DMA请求映像
要使DMA与外设建立有效连接,这不是DMA自身的事情,是各个外设的事情,每个外设都有 一个
xxx_DMACmd(XXXx,Enable )函数,如果使DMA与ADC建立有效联系,就使用 ADC_DMACmd
(ADC1,Enable); (这里我启用了ADC中的ADC1模块)。
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_A
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_V&&&
//u16& AD_Value[2];&& 不加&应该也可以& 数组名 代表地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 2;&&& & //############## 改了
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_D
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_E& //##############&&& &改了
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfW
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfW
DMA_InitStructure.DMA_Mode = DMA_Mode_C
DMA_InitStructure.DMA_Priority = DMA_Priority_H
DMA_InitStructure.DMA_M2M = DMA_M2M_D
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_I
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_N
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_R
ADC_InitStructure.ADC_NbrOfChannel = 2;&&& & //##############&&& &改了
ADC_Init(ADC1, &ADC_InitStructure);
//内部温度传感器& 添加这一句&
ADC_TempSensorVrefintCmd(ENABLE);
//##############&&& &改了
//################ Channel 10(电位器)
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
//###### 内部温度传感器& Channel 16 ###################
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5);
& 使能ADC1的DMA请求映像
& ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
&& //使用之前一定要校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
【】【】【】【】
上一篇:下一篇:
CopyRight @
单片机教程网
, All Rights Reserved3265人阅读
STM32(22)
1.我们知道DMA可以自动的不在CPU干预下,自动把数据重外设存储到内存(我们这节讲的),内存到外设,内存到内存等。但是DMA接收的是指定长度的,在接收不定长数据的时候DMA就傻眼了。网上有许多方法讲解运用定时器超时检测来接收不定长数据,而我们现在要讲的是运用串口空闲中断+DMA的方式接收不定长数据。
2.我们调试用的是串口1、DMA_Channel_4。具体的配置见下面程序:
DMA接收配置:
void USART1_DMA_Config(void)
DMA_InitTypeDef DMA_InitS
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);//开启DMA时钟
DMA_InitStructure.DMA_Channel = DMA_Channel_4;//通道4
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART1_DR_A //外设地址为[#define USART1_DR_Address    (0xx04)]
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Data_B //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToM//外设到内存
DMA_InitStructure.DMA_BufferSize = 65535; //缓冲大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_D//外设地址不增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_E//内存地址增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_B//外设数据大小1byte
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_B//内存数据大小1byte
DMA_InitStructure.DMA_Mode = DMA_Mode_C//循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_H//优先级高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_D// 不开fifo
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfF //
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_S//
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_S//
DMA_Init(DMA2_Stream5, &DMA_InitStructure);//初始化dma2流5
DMA_Cmd(DMA2_Stream5, ENABLE);//开启dma2流5
}串口1相关配置及空闲中断配置:
void STM_EVAL_COMInit(void)
GPIO_InitTypeDef GPIO_InitS
NVIC_InitTypeDef
NVIC_InitS
USART_InitTypeDef USART_InitS
/* Enable GPIO clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Enable UART clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* Connect PXx to USARTx_Tx*/
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
/* Connect PXx to USARTx_Rx*/
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
/* Configure USART Tx as alternate function
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART Rx as alternate function
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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_N
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* USART configuration */
USART_Init(USART1, USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 11;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
/* Enable USART */
USART_Cmd(USART1, ENABLE);
串口相关配置总函数:
void USART_CONFIG(void)
STM_EVAL_COMInit();
USART1_DMA_Config();//主要是配置外设地址和内存地址以及缓冲区大小,配置好后DMA就会自动的把串口数据存到相应的内存地址。
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);配置串口1DMA接收
}串口1空闲中断服务函数:
void USART1_IRQHandler(void)
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//如果为空闲总线中断
i = USART1-&SR;
i = USART1-&DR;
USART_ClearITPendingBit(USART1, USART_IT_IDLE);
DMA_Cmd(DMA2_Stream5, DISABLE);//关闭DMA,防止处理其间有数据
Data_Len=65535-DMA_GetCurrDataCounter(DMA2_Stream5);
data_recv_flag_set(); //标志位
printf(&Data_Len=%d\n\r&,Data_Len);
//DMA_Cmd(DMA2_Stream5, ENABLE);//开启DMA
} 这里需要说明下,我们在配置DMA的时候,数据大小是一个字节,内存缓冲区为65535(特别强调下:DMA最大的缓冲区为65535,再大就溢出了。YY下,如果DMA接收缓冲区上M那就更加好了)。
函数DMA_GetCurrDataCounter(DMA2_Stream5);是获取当前指针计数值,用内存缓冲区大小 - 此计数值 = 接收到的数据长度(这里单位为字节)。需要说明下在读取数据长度的时候需要先把接收DMA关闭,读取完了或者是数据处理完了在打开接收DMA,防止在处理的过程中有数据到来而出错。
大家可以用串口调试助手测试下,记住把旁边的send as hex打上勾,然后发送不定长数据,每次发送完就会进空闲中断,并把接收的数据长度打印出来。如果需要读取数据,可以从Data_Buffer(前面DMA配置的内存地址)中读取出来。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:113391次
积分:1465
积分:1465
排名:千里之外
原创:22篇
转载:62篇
评论:26条
(1)(4)(21)(6)(2)(1)(5)(12)(3)(3)(1)(8)(2)(14)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'__STM32__请进,请教几个DMA问题
1.使用DMA时,TC及HT中断是何时发生的呢???
譬如这样的情况:
通过DMA将Memory中的数据传送给peripheral:TIM1的CCR1寄存器,是传送一个DMA_PeripheralDataSize_HalfWord后产生一次TC中断呢还是传输TIM1_DMA_Buffer_Size个DMA_PeripheralDataSize_HalfWord后才产生一个TC中断呢?
2.传送一个DMA_PeripheralDataSize_HalfWord后,是不是马上按照DMA的时钟来传送下一个DMA_PeripheralDataSize_HalfWord给TIM1的CCR1寄存器呢?
如果是这样,那如果TIM1的时钟较慢,DMA更新了CCCR1后,TIM1计数还未达到更新的值,此时DMA又传输新的值给CCR1寄存器,这种情况该怎么解决呢?
——————————————————————————————————————————————————————————————
针对下面的代码,该代码截取自ST固件库STM32F10x_StdPeriph_Lib_V3.1.2\Project\STM32F10x_StdPeriph_Examples\TIM\DMA目录下的main.c文件
&&/* DMA1 Channel5 Config */
&&DMA_DeInit(DMA1_Channel5);
&&DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR3_A
&&DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SRC_B
&&DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
&&DMA_InitStructure.DMA_BufferSize = TIM1_DMA_Buffer_S
&&DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_D
&&DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_E
&&DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfW
&&DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfW
&&DMA_InitStructure.DMA_Mode = DMA_Mode_C
&&DMA_InitStructure.DMA_Priority = DMA_Priority_H
&&DMA_InitStructure.DMA_M2M = DMA_M2M_D
&&DMA_Init(DMA1_Channel5, &DMA_InitStructure);
使用DMA来给USART1传输数据时,也存在上述的第二个问题
功能已经用起来,但不是很明白
还请指教~~
如果DMA传输完成的中断标志,只会产生一次,也就是你的数据全部传输完的时候,并不是每发送一个就产生一次。
——————————————————————————————————————————————————————
(原文件名:DMA中断原理探究.png)
(原文件名:DMA中断原理探究1.png)
你想想看,如果DMA每传送一次数据就产生一次中断,要DMA有什么意思呢? 当然是传送完成(或传送一半)才产生中断。
你得到很多中断是因为使用了DMA_Mode_Circular模式。
(原文件名:STM32_DMA_Configuration.GIF)
使用了DMA_Mode_Circular模式,应该是每传输3000个HalfWord然后才产生一个TC中断
这样说来,那在ISR中读取CNDTR寄存器的值,应该都是3000或其附近的值,而不是我上面图中的“从3000不规则递减到0”的呀
你看一下中断状态寄存器的内容,是否除了TC还有其它中断条件;如果只是TC条件,是否每次退出中断处理程序时都清除了这个标志,而不是重复地进入。
使用DMA来更新TIM1的CCR1寄存器以得到频率不变、占空比变化的PWM波(或者占空比为50%、频率不同的PWM波)时:
传送一个DMA_PeripheralDataSize_HalfWord大小的数据后,是不是马上按照DMA的时钟来传送下一个DMA_PeripheralDataSize_HalfWord大小的数据给TIM1的CCR1寄存器呢?
如果是这样,那如果TIM1的时钟较慢,DMA更新了CCR1后,TIM1计数还未达到更新的值,此时DMA又传输新的值给CCR1寄存器,这种情况该怎么解决呢?
我看了这个ST固件库中的例程,不是很明白,也因这个历程有了上楼中的困惑
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define TIM1_CCR3_Address& & 0x40012C3C
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
TIM_TimeBaseInitTypeDef&&TIM_TimeBaseS
TIM_OCInitTypeDef&&TIM_OCInitS
uint16_t SRC_Buffer[3] = {, 511};
ErrorStatus HSEStartUpS
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void DMA_Configuration(void);
/* Private functions ---------------------------------------------------------*/
&&* @brief& &Main program
&&* @param&&None
&&* @retval None
int main(void)
&&/* System Clocks Configuration */
&&RCC_Configuration();
&&/* GPIO Configuration */
&&GPIO_Configuration();
&&/* DMA Configuration */
&&DMA_Configuration();
&&/* TIM1 DMA Transfer example -------------------------------------------------
&&TIM1CLK = 72 MHz, Prescaler = 0, TIM1 counter clock = 72 MHz
&&The TIM1 Channel3 is configured to generate a complementary PWM signal with
&&a frequency equal to: TIM1 counter clock / (TIM1_Period + 1) = 17.57 KHz and
&&a variable duty cycle that is changed by the DMA after a specific number of
&&Update DMA request.
&&The number of this repetitive requests is defined by the TIM1 Repetion counter,
&&each 3 Update Requests, the TIM1 Channel 3 Duty Cycle changes to the next new
&&value defined by the SRC_Buffer .
&&-----------------------------------------------------------------------------*/
&&/* TIM1 Peripheral Configuration --------------------------------------------*/
&&/* Time Base configuration */
&&TIM_TimeBaseStructure.TIM_Prescaler = 0;
&&TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
&&TIM_TimeBaseStructure.TIM_Period = 4095;
&&TIM_TimeBaseStructure.TIM_ClockDivision = 0;
&&TIM_TimeBaseStructure.TIM_RepetitionCounter = 2;
&&TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
&&/* Channel 3 Configuration in PWM mode */
&&TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
&&TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_E
&&TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_E
&&TIM_OCInitStructure.TIM_Pulse = 127;
&&TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_L
&&TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_L
&&TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_S
&&TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_R
&&TIM_OC3Init(TIM1, &TIM_OCInitStructure);
&&/* TIM1 Update DMA Request enable */
&&TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
&&/* TIM1 counter enable */
&&TIM_Cmd(TIM1, ENABLE);
&&/* Main Output Enable */
&&TIM_CtrlPWMOutputs(TIM1, ENABLE);
&&while (1)
&&* @brief&&Configures the different system clocks.
&&* @param&&None
&&* @retval None
void RCC_Configuration(void)
&&/* Setup the microcontroller system. Initialize the Embedded Flash Interface,&&
& &&&initialize the PLL and update the SystemFrequency variable. */
&&SystemInit();
&&/* TIM1, GPIOA and GPIOB clock enable */
&&RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA |
& && && && && && && && & RCC_APB2Periph_GPIOB, ENABLE);
&&/* DMA clock enable */
&&RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
&&* @brief&&Configure the TIM1 Pins.
&&* @param&&None
&&* @retval None
void GPIO_Configuration(void)
&&GPIO_InitTypeDef GPIO_InitS
&&/* GPIOA Configuration: Channel 3 as alternate function push-pull */
&&GPIO_InitStructure.GPIO_Pin =&&GPIO_Pin_10;
&&GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
&&GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
&&GPIO_Init(GPIOA, &GPIO_InitStructure);
&&/* GPIOB Configuration: Channel 3N as alternate function push-pull */
&&GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
&&GPIO_Init(GPIOB, &GPIO_InitStructure);
&&* @brief&&Configures the DMA.
&&* @param&&None
&&* @retval None
void DMA_Configuration(void)
&&DMA_InitTypeDef DMA_InitS
&&/* DMA1 Channel5 Config */
&&DMA_DeInit(DMA1_Channel5);
&&DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR3_A
&&DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SRC_B
&&DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
&&DMA_InitStructure.DMA_BufferSize = 3;
&&DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_D
&&DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_E
&&DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfW
&&DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfW
&&DMA_InitStructure.DMA_Mode = DMA_Mode_C
&&DMA_InitStructure.DMA_Priority = DMA_Priority_H
&&DMA_InitStructure.DMA_M2M = DMA_M2M_D
&&DMA_Init(DMA1_Channel5, &DMA_InitStructure);
&&/* DMA1 Channel5 enable */
&&DMA_Cmd(DMA1_Channel5, ENABLE);
关于6楼的问题
你的DMA触发条件是什么?你这个问题需要使用TIM1的更新事件触发DMA传输。
回复【5楼】__STM32__&&
你看一下中断状态寄存器的内容,是否除了TC还有其它中断条件;如果只是TC条件,是否每次退出中断处理程序时都清除了这个标志,而不是重复地进入。
-----------------------------------------------------------------------
请看我在2L中的第一幅图片
现在我添加上清除HT中断标志位,程序运行结果依旧
&&DMA_ClearITPendingBit(DMA_IT_TC);
&&DMA_ClearITPendingBit(DMA_IT_HT);
你看过中断状态寄存器的内容吗?
回复【10楼】__STM32__&&
你看过中断状态寄存器的内容吗?
-----------------------------------------------------------------------
这个问题我找到原因了,正确的代码如下:
&&if(DMA_GetITStatus(DMA1_IT_TC5))
& & DMA_ClearITPendingBit(DMA1_IT_GL5);
& & & & ncount++;
& & & & printf(&\r\n%d\t&,ncount);
& & & & printf(&%d\r\n&,DMA1_Channel5-&CNDTR);
主要有两个问题:
1.清除中断标识位不对,因为是由于通过Global Interrupt中断标志来判断是否执行ISR,所以必须这样做
DMA_ClearITPendingBit(DMA1_IT_GL5);
而不是这样:
DMA_ClearITPendingBit(DMA_IT_TC);
2.此时需要使用DMA1_IT_GL5来对具体的中断标志位进行操作,而不是DMA_IT_TC
回复【10楼】__STM32__&&
你看过中断状态寄存器的内容吗?
-----------------------------------------------------------------------
中断状态寄存器中,TC、HT、GL都是1
但是我只允许了TC中断
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
回复【10楼】__STM32__&&
另:我想想看看HT中断时在何时产生的(主要是想看看传输奇数个数据时HT是在何处产生)
通过在HT中断服务子程序中打印一些数据不现实,不知可有其他方法
回复【8楼】__STM32__&&
关于6楼的问题
你的DMA触发条件是什么?你这个问题需要使用TIM1的更新事件触发DMA传输。
-----------------------------------------------------------------------
(原文件名:DMA_Request.png)
稍微理解了一些
感觉那些触发源的作用就如上面我所说的
不仅仅建立外设与Mem的连接,而且还作为传输一次DMA_MemoryDataSize_HalfWord数据的命令
触发源的每一次触发是启动一次数据传输,而不是启动整个DMA传输。
回复【15楼】lofeng
-----------------------------------------------------------------------
最近频频发现lofeng的身影,你在学习ST的片子?顶一个
我也被DMA问题困扰好几天了,一用他就出现死机,问题还没找到哦。仔细研究下
__STM32__的说法是正确的。
& & DMA的触发源,就是触发一资DMA传输。
& & 在1个DMA通道上,如果该DMA的触发源同时有多个存在,且这几个触发源都有可能触发DMA传输,那么,其结果一般情况下是不正确的。
& & 换言之,在一次DMA传输中,只允许存在一个触发源,设置该触发源的外设数据寄存器为DMA的外设寄存器。当触发条件满足时,完成一个数据的传输。如果设置在中断条件,则在满足中断条件的情况,触发中断。在计数寄存器的值为0时,不再传输数据。在DMA允许、且完成中断允许、完成中断标志没清0,将触发中断(一次传输完成后,如果不清除DMA的允许位,将频繁触发中断,所以在中断处理程序中要清0中断标志位)。
mark,学习
我一直有一个疑问,就是USART_DMA发送,能不能间隔一定时间(比如10MS)发送一个字节?因为一个字节紧接着一个字节的发送,发送的太快了,接收方可能处理不及.
现在终于知道可以了.
也遇到dma的问题,问题描述如下:
1、使用stm32f103的2个i2s和codec芯片相连,产生正弦波的同时得到codec的adc数值,stm32控制codec芯片使用dma方式。
2、想调试状态下看到adc数值。但是,stm32 进入swd联机状况下,发现dma不能正常工作(不能在示波器上看到正弦波),从而也无法看到
正确adc数值。
3、猜测可能dma工作时和swd发生总线冲突,果然,stm32在不联机的情况下,dma可以正确运行。-----这事搞得我很郁闷。只好先把adc数
据保存sd卡里,然后传到笔记本上查看数据。
请问各位大侠,有什么办法能jtag(swd)连线看到测量数值?(哦,我使用是iar。)
【31楼】 fickle :
方法1.把AD数据用DA输出,用示波器查看.
方法2.把AD数据用USART输出到电脑上.
回复【33楼】&&
【31楼】 fickle :
方法1.把ad数据用da输出,用示波器查看.
方法2.把ad数据用usart输出到电脑上.
-----------------------------------------------------------------------
谢谢你的回复。
方法1.把ad数据用da输出,用示波器查看.
--恩,dac可以产生完美正弦波(示波器看),但是和jlink联机时,就没有波形了。
方法2.把ad数据用usart输出到电脑上.
--呵呵,没接232。不过,有sdcard保存后,在笔记本看数据。excel数据也是很好的正弦波。
jlink联机下,如何保证i2s正常工作?
弱弱的问下楼至
#define TIM1_CCR3_Address& & 0x40012C3C
这个地址是在哪里找的?谢谢!
Mark!学习中!!!
学习了,看完帖子有加深了DMA的了解
阿莫电子论坛, 原"中国电子开发网"

我要回帖

更多关于 可用于中断处理 的文章

 

随机推荐