注:这里我们假设的ADC配置为:
单次触发/不连续转换,扫描模式,单adc模式,两个通道,且数据对齐
DMA_BufferSize
/*------------------------- DMAy Streamx NDTR Configuration ----------------*/
/* Write to DMAy Streamx NDTR register */
DMAy_Streamx->NDTR = DMA_InitStruct->DMA_BufferSize;
关于这个参数的理解,我们还需要阅读参考手册的对于章节
1.
9.3.2 DMA 事务
DMA 事务由给定数目的数据传输序列组成。要传输的数据项的数目及其宽度(8 位、16 位
或 32 位)可用软件编程。
每个 DMA 传输包含三项操作:
● 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,从外设数据寄存器或存储器单元中
加载数据。
● 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,将加载的数据存储到外设数据寄存
器或存储器单元。
● DMA_SxNDTR 计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数。
在产生事件后,外设会向 DMA 控制器发送请求信号。DMA 控制器根据通道优先级处理该请
求。只要 DMA 控制器访问外设,DMA 控制器就会向外设发送确认信号。外设获得 DMA 控
制器的确认信号后,便会立即释放其请求。一旦外设使请求失效,DMA 控制器就会释放确
认信号。如果有更多请求,外设可以启动下一个事务。
我们主要关注其手册的红色说明
2.
如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或
DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。
3.
上述三个是关于DMA_BufferSize的具体在使用中的说明
我们会发现:什么叫做事务数?DMA请求搬运和这个又有什么关系呢?
DMA 事务由给定数目的数据传输序列组成。
而这个与DMA请求搬运有什么关系呢?
我们先举个例子,
// 外设数据大小为半字,即两个字节
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
//同外设数据大小一样
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
上述的配置,为需要搬运的数据和搬运到的每一个存储空间大小一样的前提下
需求/逻辑关系1:
当ADC的规则组通道数为一个时,若是由外部触发来使ADC开始转换,而后数据存储到DR寄存器中(规则组的寄存器有且只有一个),我们需要DMA来搬运每次由外部触发得到的ADC转换的值,并显示.(大致是这个意思,具体配置先不用管)
而这里DMA的请求,目的是防止每次外部触发的ADC转换的值,被覆盖
这里每次的DMA请求为一次,DMA搬运的总工作量为多少呢?
答案是:1
而我们这里的DMA 事务指的就是每次的DMA请求为一次,DMA搬运的总工作量
这里还不能很好的体现出DMA事务和DMA搬运的关系
需求/逻辑关系2:
我们在以上 需求/逻辑关系1 的基础追加一个规则通道,其他不变
这里也需要高效的DMA来协助ADC转换工作,防止数据被覆盖
也即:
通道1,转换完成后,数据存储到DR寄存器中,我们理应需要一次DMA请求
通道2,转换完成后,数据存储到DR寄存器中,我们理应也需要一次DMA请求
这里,我们先回顾一下:前面的需求/逻辑关系1
得知:我们是外部触发ADC转换完,而ADC才触发DMA请求
那么,视乎和上述我们需求/逻辑关系2 中的需要不符合
我们应该是
通道1——》DMA请求
通道2——》DMA请求
难道我们是需要通过标志位来检测每一个通道的转换情况,而进行DMA请求吗,
这在功能实现方面可能吗——是可能的
不过这ADC转换的速度是极高的,且每次的DMA请求又是效率极低的方式
因此:引出,我们上述的DMA 事务指的就是每次的DMA请求为一次,DMA搬运的总工作量
这里ADC请求的情况是 外设发送DMA请求,事先配置好的 DMA_SxNDTR,DMA开始搬运工作,当一次DMA搬运数据结束后, DMA_SxNDTR递减1,如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止
这里的需求就是:DMA_BufferSize配置为2,一次DMA请求,事务为2,即进行两次的DMA搬运工作。
而后,在写这个的过程中,我们又有了一个顾虑:
就是外部触发ADC开始转换,而后扫描通道2,转换完成才进行DMA搬运,那数据不是早就覆盖了吗,即使上述的事务数为2,但同时实验现象又是对的,这是为什么?
我们从下述找到了依据:
/**
* @brief Enables or disables the ADC DMA request after last transfer (Single-ADC mode)
* @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
* @param NewState: new state of the selected ADC DMA request after last transfer.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void ADC_DMARequestAfterLastTransferCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_ADC_ALL_PERIPH(ADCx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the selected ADC DMA request after last transfer */
ADCx->CR2 |= (uint32_t)ADC_CR2_DDS;
}
else
{
/* Disable the selected ADC DMA request after last transfer */
ADCx->CR2 &= (uint32_t)(~ADC_CR2_DDS);
}
}
查看参考手册:
以上,我们知道了我们使能了DMA请求,只要配置了事务数,而配置了DDS这个位
则会来进行不断地检测工作,只要发生数据转换且 DMA = 1,便会发出 DAM 请求
这里,我们也就是说实现了
通道1——》DMA请求
通道2——》DMA请求
但不是我们手动去实现的,是通过DDS位来实现的
那么结合上述的,我们尝试这描述一下上述的过程:
DMA_BufferSize为 DMA 事务由给定数目的数据传输序列组成,也即某一个外设通过通道优先级获得了对于DMA总线请求的占用(DMA也是一个总线结构,有点类似I2C硬件结构),而执行DMA事务,每进行一次ADC通道的DMA传输数据后,DMA事务/DMA_BufferSize/DMA_SxNDTR 计数器递减,若有更多的请求(DMA_SxNDTR 计数器不为0)外设可以启动下一个事务,也即下一次的DMA控制器的请求信号,直至DMA_SxNDTR 计数器为0,该外设释放DMA控制总线。
ADC_DMAAccessMode
ADC_DMAAccessMode和DMA_BufferSize 还有数据大小的关系有关,下期再说