STM32f103 系列有 3 个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道。其中ADC1 和 ADC2 都有 16 个外部通道,ADC3 根据 CPU 引脚的不同通道数也不同,一般都有8 个外部通道。
    ![~8K}P$T%SI]W[Q65ILRTF.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通道和外设之间有固定的地址,不同的通道和外设不能建立链接。具体如下:
    image.png
    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的配置

    1. void ADC1_DMA_config(DMA_Channel_TypeDef* DMAy_Channelx,u32 memAddr,u32 periphAddr,u8 bufSize)
    2. {
    3. DMA_InitTypeDef DMA_InitStructure;
    4. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能
    5. //DMA_DeInit(DMAy_Channelx);
    6. /* 初始化DMA */
    7. DMA_InitStructure.DMA_PeripheralBaseAddr = periphAddr;//外设地址
    8. DMA_InitStructure.DMA_MemoryBaseAddr = memAddr;//内存地址
    9. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设是源地址
    10. DMA_InitStructure.DMA_BufferSize = bufSize;//一次DMA传输的字节量
    11. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不增加
    12. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址增加
    13. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//adc的DR是16位的
    14. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//按half word
    15. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 正常模式,传输完一次后数据量不自动重载
    16. DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//
    17. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //
    18. DMA_Init(DMAy_Channelx, &DMA_InitStructure);//DMA初始化
    19. NVIC_InitTypeDef NVIC_InitStruct;
    20. NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel1_IRQn; //ADC1挂在DMA的通道1上
    21. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    22. NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
    23. NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    24. NVIC_Init(&NVIC_InitStruct);
    25. DMA_ITConfig(DMAy_Channelx,DMA_IT_TC,ENABLE);//DMA chaneel interrupt EN
    26. }

    ADC配置

    1. void ADC1_init(void)
    2. {
    3. ADC_InitTypeDef ADC_InitStruct;
    4. GPIO_InitTypeDef io;
    5. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA, ENABLE);
    6. RCC_ADCCLKConfig(RCC_PCLK2_Div6);//这里一定不要忘记,这里有单独的ADC分频控制器
    7. io.GPIO_Mode = GPIO_Mode_AIN;//PA0 PA1分别作为ADC1的通道0 通道1
    8. io.GPIO_Pin = (GPIO_Pin_0|GPIO_Pin_1);
    9. GPIO_Init(GPIOA, &io);
    10. ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
    11. ADC_InitStruct.ADC_ScanConvMode = ENABLE;//开启扫描模式
    12. ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//close continuous mode
    13. ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    14. ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    15. ADC_InitStruct.ADC_NbrOfChannel = 2;//两个通道
    16. ADC_Init(ADC1, &ADC_InitStruct);
    17. ADC_Cmd(ADC1, ENABLE);//adc使能
    18. ADC_DMACmd(ADC1, ENABLE);//dma请求使能
    19. ADC_ResetCalibration(ADC1);//复位校准
    20. while(ADC_GetResetCalibrationStatus(ADC1));//等待复位转换结束
    21. ADC_StartCalibration(ADC1);//开始校准
    22. while(ADC_GetCalibrationStatus(ADC1));//等待开始校准结束
    23. ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);//设置采样通道的次序,有几个通道调用几次这个函数
    24. ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
    25. }
    1. //设置DMA传送数量并用软件触发adc。
    2. void app_ADC1DMA_enable(void)
    3. {
    4. DMA_Cmd(DMA1_Channel1, DISABLE);//DMA channel enable
    5. DMA_SetCurrDataCounter(DMA1_Channel1, 2);
    6. DMA_Cmd(DMA1_Channel1, ENABLE);
    7. //因为没有开始DMA回环模式,每次传送完后,DMA_CNDTRx寄存器清零,这时不管DMA使能与否DMA都会停止,因此要重新设置传送的数据量,再此之前一定要先关闭DMA通道,切记。
    8. ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发
    9. }
    10. //中断函数
    11. void DMA1_Channel1_IRQHandler(void)
    12. {
    13. if(DMA_GetFlagStatus(DMA1_FLAG_TC1))
    14. {
    15. dma1FinishFlag = 1;
    16. DMA_ClearFlag(DMA1_FLAG_TC1);
    17. }
    18. }

    主函数

    1. int main()
    2. {
    3. extern u8 dma1FinishFlag;
    4. ST_USART1_RCV_BUF rxbuf={{0},0,0,0};
    5. u32 temp = 0;
    6. u8 i;
    7. //float fval;
    8. SysTick_Init(72);
    9. NVIC_PriorityGroupConfig(2);
    10. LED_Init();
    11. usart1_config(9600);
    12. ADC1_init();
    13. ADC1_DMA_config(DMA1_Channel1,(u32)rxbuf.buf,(u32)&ADC1->DR,2);
    14. app_ADC1DMA_enable();
    15. while(1)
    16. {
    17. temp = 0;
    18. if(dma1FinishFlag)
    19. {
    20. dma1FinishFlag = 0;//清空标志位
    21. printf("voltage1 is %f\n",(rxbuf.buf[1]<<8|rxbuf.buf[0])/4096.0*3.3);
    22. printf("voltage2 is %f\n",(rxbuf.buf[3]<<8|rxbuf.buf[2])/4096.0*3.3);
    23. app_ADC1DMA_enable();//开始下一次传输
    24. delay_ms(1000);
    25. PBout(11)=~PBout(11);
    26. }
    27. }
    28. }