一、简介
??本例程以FM33LE026为例,已经过验证,其他型号不保证适用。 ??工欲善其事必先利其器,官方手册就是利器,在接触新开发平台时,仔细阅读手册能大幅提升开发进度,避免很多掉头发事件,光头强除外。
- FM33LE0系列带有2Msps 12bit SAR-ADC,可实现温度、内部基准和外部输入信号的测量功能。主要特点为:
- 工作电压 1.65~5.5V;
- 输入信号幅度 0~ VREF+;
- 最高采样率 2Msps(FADC=32MHz);
- 16 个单端输入通道, 包含温度传感器、 内部基准电压、 12 个外部通道;
- 12 个外部通道(最大 2Msps), 2 个内部通道(温度传感器通道、 AVREF 采样通道);
- 可配置的采样保持时间;
- 支持单次转换和连续转换;
- 支持 DMA;
- 支持过采样硬件平均, 最高 16bit 输出(256 次平均);
- ADC 支持以下转换模式:
- 单次转换
- 半自动触发(SEMI-AUTOMATIC);
- 全自动触发(AUTOMATIC);
- 连续转换
??转换启动可以由软件或事件触发,通过寄存器选择多个事件触发源。
- 在多通道转换或连续转换时,使用 DMA 进行转换结果搬移是高效的解决方案。在使能了 DMAEN 的情况下,当每次转换完成后(EOC),ADC controller 模块会产生一个 DMA 请求,通知 DMA 将数据寄存器中的结果搬运到指定的 SRAM 地址。 ADC 的 DMA 接口支持单次模式和循环模式:
??在全自动触发模式下(ADC_CFGR2.SEMI=0)和半自动触发模式下(ADC_CFGR2.SEMI=1), ADC 的 DMA接口都可以支持单次模式和循环模式。
- 单次模式
转换完成后发起数据搬运,此过程会一直重复,直到软件配置的 DMA 传输长度完成,然后 ADC 控制器会自动停止转换(通过接收 DMA 的传输完成中断标志信号),关闭 ADC,不再向 DMA 发起请求。此模式主要用于对特定模拟信号进行一定长度的采样。 - 循环模式
与 DMA 的循环模式相配合, ADC 不断循环转换并发起 DMA 请求,直到软件停止转换。此模式可以用于处理连续不断模拟信号采样。
- FM33LE0 系列 DMA 主要特点为:
- 7通道外设 PDMA,支持 Peripherals<>RAM 传输;
- 1通道存储器 MDMA,支持 Flash<>RAM 传输;
- 外设 DMA 传输由外设请求触发, DMA 工作期间不影响 CPU 运行;
- 外设通道最大传输长度65536字节(64KB),支持 byte/half-word/word 传输;
- Flash->RAM 通道最大传输长度8192字节,只支持 word 传输;
- 支持 Flash 连续编程(RAM->Flash),需要预先进行擦除,一次编程固定为256字节;
- RAM 指针递增、递减;
- 可产生半程中断和全程中断;
- 通道优先级可配置(4级优先级);
- DMA 工作流程如图所示:
- 外设 DMA 通道支持循环模式(Circular mode)。循环模式下,当 CHxTSIZE 寄存器定义的传输长度完成后, DMA 不会自动停止,而是返回 RAM 指针寄存器定义的起始地址,继续传输。 DMA 的半程中断和全程中断还是会正常置起, DMA 不会终止传输,直到软件关闭通道。
??结合手册描述和框图可知,ADC 支持 DMA 搬运,为了能够随时取值,ADC 应配置成连续转换模式。同时 DMA 应配置为循环模式,以实时刷新指定的 RAM 区域。
二、代码实现
??可以明确 ADC 工作在连续转换模式下,DMA 工作在循环模式下。
- ADC 配置
static void adc_gpio_init(void)
{
FL_GPIO_InitTypeDef GPIO_InitStruct;
FL_ADC_InitTypeDef Sampling_InitStruct;
FL_ADC_CommonInitTypeDef CommonInitStruct;
GPIO_InitStruct.pin = ADC_S1_PIN | ADC_S2_PIN | ADC_S3_PIN;
GPIO_InitStruct.mode = FL_GPIO_MODE_ANALOG;
GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.pull = FL_DISABLE;
GPIO_InitStruct.remapPin = FL_DISABLE;
FL_GPIO_Init(ADC_GPIO_PORT, &GPIO_InitStruct);
CommonInitStruct.clockSource = FL_RCC_ADC_CLK_SOURCE_RCHF;
CommonInitStruct.clockPrescaler = FL_RCC_ADC_PSC_DIV8;
FL_ADC_CommonInit(&CommonInitStruct );
Sampling_InitStruct.conversionMode = FL_ADC_CONV_MODE_CONTINUOUS;
Sampling_InitStruct.autoMode = FL_ADC_SINGLE_CONV_MODE_AUTO;
Sampling_InitStruct.waitMode = FL_DISABLE;
Sampling_InitStruct.overrunMode = FL_DISABLE;
Sampling_InitStruct.scanDirection = FL_ADC_SEQ_SCAN_DIR_FORWARD;
Sampling_InitStruct.externalTrigConv = FL_ADC_TRIGGER_EDGE_NONE;
Sampling_InitStruct.triggerSource = FL_ADC_TRGI_PA8;
Sampling_InitStruct.fastChannelTime = FL_ADC_FAST_CH_SAMPLING_TIME_16_ADCCLK;
Sampling_InitStruct.lowChannelTime = FL_ADC_SLOW_CH_SAMPLING_TIME_16_ADCCLK;
Sampling_InitStruct.oversamplingMode = FL_DISABLE;
Sampling_InitStruct.overSampingMultiplier = FL_ADC_OVERSAMPLING_MUL_16X;
Sampling_InitStruct.oversamplingShift = FL_ADC_OVERSAMPLING_SHIFT_4B;
FL_ADC_Init(ADC, &Sampling_InitStruct);
FL_ADC_EnableSequencerChannel(ADC, ADC_S1_CH);
FL_ADC_EnableSequencerChannel(ADC, ADC_S2_CH);
FL_ADC_EnableSequencerChannel(ADC, ADC_S3_CH);
FL_ADC_EnableDMAReq(ADC);
}
- DMA 配置
#define ADC_CH_NUM 3
uint16_t adc_temp[ADC_CH_NUM] = {0};
static void adc_dma_init(void)
{
FL_DMA_InitTypeDef DMA_InitStruct = {0};
FL_DMA_ConfigTypeDef DMA_ConfigStruct = {0};
DMA_InitStruct.periphAddress = FL_DMA_PERIPHERAL_FUNCTION1;
DMA_InitStruct.direction = FL_DMA_DIR_PERIPHERAL_TO_RAM;
DMA_InitStruct.memoryAddressIncMode = FL_DMA_MEMORY_INC_MODE_INCREASE;
DMA_InitStruct.dataSize = FL_DMA_BANDWIDTH_16B;
DMA_InitStruct.priority = FL_DMA_PRIORITY_HIGH;
DMA_InitStruct.circMode = FL_ENABLE;
FL_DMA_Init(DMA, &DMA_InitStruct, DMA_ADC_CH);
DMA_ConfigStruct.memoryAddress = (uint32_t)&adc_temp;
DMA_ConfigStruct.transmissionCount = ADC_CH_NUM - 1;
FL_DMA_StartTransmission(DMA, &DMA_ConfigStruct, DMA_ADC_CH);
}
- 全局初始化
void adc_init(void)
{
adc_gpio_init();
adc_dma_init();
FL_ADC_Enable(ADC);
FL_DMA_Enable(DMA);
FL_ADC_EnableSWConversion(ADC);
}
??配置完成后,可随时读取 ADC 值,果然拿来主义就是爽。当系统中使用了其他 DMA 功能,需注意 DMA 通道的优先级,以避免重要用户数据丢失。That’s all!
|