ADC
ADC即是模数转换器,将电压信号转换为数字信号。
以stm32f407为例,其拥有18个12位ADC转换通道,其中16个外部通道以及俩个内部通道。
ADC有单次,连续,扫描或间断模式执行转换,转换结果储存在16位的寄存器中,可选择左对齐或右对齐模式。转换周期可供选择越大越精确。
把ADC1的通道使用的GPIO引脚配置成模拟输入模式,在作为ADC的输入时,必须使用模拟输入。对于ADC通道,每个ADC通道对应一个GPIO引脚端口,GPIO的引脚在设为模拟输入模式后可用于模拟电压的输入。STM32F407ZET6有三个ADC,这三个ADC公用16个外部通道。此外在配置过程中,可配置16个规则通道以及4个注入通道,所谓注入通道即类似于中断的一种插入读取。
ADC配置流程:
1.使能io时钟和adc时钟(不用使能端口复用功能,adc挂载在APB2总线下,只有在其他总线下的外外设才需要端口复用)
2.设置adc时钟分配因子以确定adc工作时钟频率。
3.重置adc寄存器。
4.初始化adc结构体参数。
5.使能adc。
6.开启复位校准。
7.开启ad校准。
此处为6路ADC连续扫描加DMA连续转换初始化程序
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 6;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = 4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = 5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = 6;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC1)
{
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
}
}
DMA
DMA,全称Direct Memory Access ,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送的同类,能使CPU的效率大大提高。STM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。
DMA数据流
8 个 DMA 控制器数据流都能够提供源和目标之间的单向传输链路。每个数据流配置后都可以执行: ● 常规类型事务:存储器到外设、外设到存储器或存储器到存储器的传输。 ● 双缓冲区类型事务:使用存储器的两个存储器指针的双缓冲区传输(当 DMA 正在进行自/至缓冲区的读/写操作时,应用程序可以进行至/自其它缓冲区的写/读操作)。要传输的数据量(多达 65535)可以编程,并与连接到外设 AHB 端口的外设(请求 DMA 传输)的源宽度相关。每个事务完成后,包含要传输的数据项总量的寄存器都会递减。
指针递减
根据 DMA_SxCR 寄存器中 PINC 和 MINC 位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。 通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。如果使能了递增模式,则根据在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增 1(对于字节)、2(对于半字)或 4(对于字)。为了优化封装操作,可以不管 AHB 外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。DMA_SxCR 寄存器中的 PINCOS 位用于将增量偏移大小与外设 AHB 端口或32 位地址(此时地址递增 4)上的数据大小对齐。PINCOS 位仅对 AHB 外设端口有影响。如果将 PINCOS 位置 1,则不论 PSIZE 值是多少,下一次传输的地址总是前一次传输的地址递增 4(自动与 32 位地址对齐)。但是,AHB 存储器端口不受此操作影响。如果 AHB 外设端口或 AHB 存储器端口分别请求突发事务,为了满足 AMBA 协议(在固定地址模式下不允许突发事务),则需要将 PINC 或 MINC 位置 。
循环模式
可用于处理循环缓冲区和连续数据流(例如 ADC 扫描模式)。可以使用 DMA_SxCR?寄存器中的 CIRC 位使能此特性。当激活循环模式时,要传输的数据项的数目在数据流配置阶段自动用设置的初始值进行加载,并继续响应 DMA 请求
DMA 控制器执行直接存储器传输
因为采用 AHB 主总线,它可以控制 AHB 总线矩阵来 启动 AHB 事务。它可以执行下列事务: 1,外设到存储器的传输 2, 存储器到外设的传输 3,存储器到存储器的传输
这里特别注意一下,存储器到存储器需要外设接口可以访问存储器,而仅 DMA2 的外设接 口可以访问存储器,所以仅 DMA2 控制器支持存储器到存储器的传输,DMA1 不支持。
DMA初始化代码于ADC中已编写,只需要加上时钟使能即可,在主函数中加入ADC与DMA开启函数即可正常使用,注意DMA的使能一定要在ADC的前面否则会因为几微秒的时间差造成传输数据的错位。
数据错位
ADC的数据流只能一个一个读取因为只有一个结果储存寄存器,因此在DMA洪流中,但凡有时间线的错位,就会造成数据错位,形成一种薛定谔的数据流,各通道读取错乱并无迹可寻。若想间断读取,则必先关闭ADC外设,即判定结果转化成功之后,以DMA将数据传出时关闭ADC及DMA,若还未传出数据先关闭了ADC则会因为时钟丢失而无法读取数据,因此要在使能ADC中断在回调函数中加上禁止函数,从而使得DMA与ADC同步(即使不同步数据流也有迹可寻),当进行DMA连续转换时则不用考虑太多,为防止数据跳变,可在回调中加上禁止函数,方便数据读取。
for(a=0;a<6;a++)
{
HAL_ADC_Start_DMA(&hadc1(uint32_t*)adc_buff,6);
adcdat0[a]+=(int)adc_buff[a];
}//启动转换,并取出数据
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Stop_DMA(hadc);
}//回调函数关闭DMA
未完待续
|