STM32f103 系列有 3 个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道。其中ADC1 和 ADC2 都有 16 个外部通道,ADC3 根据 CPU 引脚的不同通道数也不同,一般都有8 个外部通道。
![~8K}P$T%SI]W[Q65
ILRTF.png](https://cdn.nlark.com/yuque/0/2021/png/2762329/1634735629733-c8ac1203-efcc-4706-ab74-7dfd67083481.png#clientId=uea2e641e-e339-4&from=drop&id=u756e9814&margin=%5Bobject%20Object%5D&name=~8K%7DP%60%24T%25SI%5DW%5BQ65%60ILRTF.png&originHeight=508&originWidth=951&originalType=binary&ratio=1&size=144658&status=done&style=none&taskId=u576ad733-5ae8-45b4-a4ab-b62828d7881)
说一下DMA
直接存储器存取(DMA)用来提供在外设(例如ADC、Timer等)和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。这说明DMA的作用范围为外设产生的数据到存储器之间,和存储器和存储器之间可以调动DMA总线。
DMA通道和外设之间有固定的地址,不同的通道和外设不能建立链接。具体如下:
DMA和外设之间大致的工作流程,就拿ADC来说,当启动了ADC_DMACmd这个函数后,DMA和ADC之间就建立啦链接。在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。
循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。在DMA_CCRx寄存器中的CIRC位用于开启这一功能。当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。每当ADC转化一次数据完成之后,DMA说,大哥你只管计算数据就啦,剩下的交给我吧,即DMA占用一下数据总线(查的手册为占用一半的资源),把数据发送到定义的二维数组地址下的存储空间。假设定义了10组两个通道大小的AD值,那么ADC会一直转换直到20组,那么在这个过程当中,CPU只管计算(比如采样、编码、量化等,这些可查看ADC)之后DMA尽快的去把数据送到二维数组当中。在这个DMA送数据的连续过程当中就为循环缓冲区。
[
](https://blog.csdn.net/lipengyu1363658871/article/details/102774852)
DMA的配置
void ADC1_DMA_config(DMA_Channel_TypeDef* DMAy_Channelx,u32 memAddr,u32 periphAddr,u8 bufSize)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能
//DMA_DeInit(DMAy_Channelx);
/* 初始化DMA */
DMA_InitStructure.DMA_PeripheralBaseAddr = periphAddr;//外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = memAddr;//内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设是源地址
DMA_InitStructure.DMA_BufferSize = bufSize;//一次DMA传输的字节量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址增加
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//adc的DR是16位的
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//按half word
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 正常模式,传输完一次后数据量不自动重载
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //
DMA_Init(DMAy_Channelx, &DMA_InitStructure);//DMA初始化
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel1_IRQn; //ADC1挂在DMA的通道1上
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
DMA_ITConfig(DMAy_Channelx,DMA_IT_TC,ENABLE);//DMA chaneel interrupt EN
}
ADC配置
void ADC1_init(void)
{
ADC_InitTypeDef ADC_InitStruct;
GPIO_InitTypeDef io;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//这里一定不要忘记,这里有单独的ADC分频控制器
io.GPIO_Mode = GPIO_Mode_AIN;//PA0 PA1分别作为ADC1的通道0 通道1
io.GPIO_Pin = (GPIO_Pin_0|GPIO_Pin_1);
GPIO_Init(GPIOA, &io);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = ENABLE;//开启扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//close continuous mode
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = 2;//两个通道
ADC_Init(ADC1, &ADC_InitStruct);
ADC_Cmd(ADC1, ENABLE);//adc使能
ADC_DMACmd(ADC1, ENABLE);//dma请求使能
ADC_ResetCalibration(ADC1);//复位校准
while(ADC_GetResetCalibrationStatus(ADC1));//等待复位转换结束
ADC_StartCalibration(ADC1);//开始校准
while(ADC_GetCalibrationStatus(ADC1));//等待开始校准结束
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);//设置采样通道的次序,有几个通道调用几次这个函数
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
}
//设置DMA传送数量并用软件触发adc。
void app_ADC1DMA_enable(void)
{
DMA_Cmd(DMA1_Channel1, DISABLE);//DMA channel enable
DMA_SetCurrDataCounter(DMA1_Channel1, 2);
DMA_Cmd(DMA1_Channel1, ENABLE);
//因为没有开始DMA回环模式,每次传送完后,DMA_CNDTRx寄存器清零,这时不管DMA使能与否DMA都会停止,因此要重新设置传送的数据量,再此之前一定要先关闭DMA通道,切记。
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发
}
//中断函数
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC1))
{
dma1FinishFlag = 1;
DMA_ClearFlag(DMA1_FLAG_TC1);
}
}
主函数
int main()
{
extern u8 dma1FinishFlag;
ST_USART1_RCV_BUF rxbuf={{0},0,0,0};
u32 temp = 0;
u8 i;
//float fval;
SysTick_Init(72);
NVIC_PriorityGroupConfig(2);
LED_Init();
usart1_config(9600);
ADC1_init();
ADC1_DMA_config(DMA1_Channel1,(u32)rxbuf.buf,(u32)&ADC1->DR,2);
app_ADC1DMA_enable();
while(1)
{
temp = 0;
if(dma1FinishFlag)
{
dma1FinishFlag = 0;//清空标志位
printf("voltage1 is %f\n",(rxbuf.buf[1]<<8|rxbuf.buf[0])/4096.0*3.3);
printf("voltage2 is %f\n",(rxbuf.buf[3]<<8|rxbuf.buf[2])/4096.0*3.3);
app_ADC1DMA_enable();//开始下一次传输
delay_ms(1000);
PBout(11)=~PBout(11);
}
}
}