使用CubeMX配置STM32F401的ADC+DMA手动触发非连续转换(LL库)
izilzty 发布于 阅读:5872
1. 期望实现的效果
(a) 手动触发转换 -> ADC开始转换并由DMA传输 -> 一定量数据转换完成 -> 等待下次触发
|---- 空闲时间执行其他程序 -> 检查是否转换完成 -> 读取数据并处理 -> (a)
2. 配置CubeMX工程
按照个人需求配置完时钟和其他外设后进入ADC配置页面:
- 勾选要转换的通道
- 在
ADC_Regular_ConversionMode
内设置要转换的通道和采样时间,可以设置多个通道。 - 在
ADC_Settings
内设置ADC时钟、分辨率、数据对齐方式等。 - 将
Scan Conversion Mode
和Continuous Conversion Mode
设置为Enable
- 将
Discontinues Conversion Mode
和DMA Continuous Requests
设置为Disable
- 将
End Of Conversion Selection
设置为EOC flag at the end of all conversions
- 其他选项保持默认即可。
关于各种转换选项大致功能描述:
Scan Conversion Mode
为每次触发后转换所有预设置的通道,F401仅有此一个选项。Continuous Conversion Mode
为触发以后连续转换不停止。Discontinues Conversion Mode
为触发后按给定的大小分次转换,例如共有6通道需要转换,可以设置为每转换2通道后停止转换。DMA Continuous Requests
为DMA内存填满后是否继续请求DMA,如果使用此选项,则需要打开DMA的Circular模式,否则会造成溢出。
ADC配置完成后点击DMA Settings选项卡进入DMA配置页面:
- 点击下方
Add
添加一个DMA通道。 DMA Request
选择ADC1
,根据需要设置优先级Priority
。- 其他选项保持默认即可。
如果需要在中断内处理数据则需要在NVIC内打开DMA全局中断。这里不使用中断,直接读取DMA的TC标志,所以不配置中断。
完成后生成代码并打开
3. 添加自定义代码
uint16_t volatile ADC_DMA_Array[想要转换的次数 * CubeMX内设置的通道数];
/* 例如转换四次两通道,则内存数据排列为:CH1, CH2, CH1, CH2, CH1, CH2, CH1, CH2 */
void ADC_DMA_Start(void)
{
volatile uint32_t adc_stable_count; /* ADC开启后等待稳定计数 */
if (LL_ADC_IsEnabled(ADC1) != SET) /* 首次使用ADC开启并等待稳定 */
{
LL_ADC_Enable(ADC1);
/* stm32f0xx_hal_adc.c */
adc_stable_count = (3 * (SystemCoreClock / 1000000U));
while (adc_stable_count != 0)
{
adc_stable_count--;
}
memset((uint8_t *)&ADC_DMA_Array, 0X00, sizeof(ADC_DMA_Array)); /* volatile显式转换消除警告 */
}
/* 停止上一次ADC转换 */
LL_ADC_REG_SetContinuousMode(ADC1, LL_ADC_REG_CONV_SINGLE); /* 停止ADC连续转换 */
while (LL_ADC_IsActiveFlag_EOCS(ADC1)) /* 等待最后一次转换完成,避免数据错位 */
{
__NOP();
}
/* 配置DMA地址 */
LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_0); /* DMA配置前需要关闭 */
LL_DMA_ClearFlag_TC0(DMA2);
LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_0, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA), (uint32_t)&ADC_DMA_Array, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_0, sizeof(ADC_DMA_Array)/sizeof(*ADC_DMA_Array));
LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_0); /* 开启DMA */
/* 准备开始ADC转换 */
LL_ADC_ClearFlag_EOCS(ADC1);
LL_ADC_ClearFlag_OVR(ADC1);
LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE); /* ADC DMA传输完成后需要手动清除 DMA Bit 并且再次设置才可以开始下一次转换 */
LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_LIMITED); /* 设置 DMA Bit */
LL_ADC_REG_SetContinuousMode(ADC1, LL_ADC_REG_CONV_CONTINUOUS); /* 启用ADC连续转换 */
LL_ADC_REG_StartConversionSWStart(ADC1); /* 开始转换 */
}
4. 主程序伪代码:
ADC_DMA_Start();
while(1)
{
空闲时间,运行其他功能();
if(LL_DMA_IsActiveFlag_TC0(DMA2)) /* 检查DMA传输是否完成 */
{
处理ADC数据();
ADC_DMA_Start();
}
}