为了自己记录
借用了江科协的代码和正点原子的代码。。
有个地方弄错了,调试好久,废话不多话,贴代码
目的
写一个基于STM32系列单片机寄存器版本的ADC+DMA可用代码
功能:
8-2江科协课程(DMA+AD多通道)代码寄存器实现
。。
善用例程,多加调试。可以利用成熟代码作为对比进行开发与调试(对于寄存器配置的数据,看看哪块没配好。ADC->CR2的位8没配好,卡了好久)
代码
代码部分,主要展示三个重要的文件,其余配套文件参考江科协例程代码(如OLED.c、OLED.h)
AD.h
#ifndef __ADC_H
#define __ADC_H
//#include "sys.h"
#include <stm32f10x.h>
#define ADC_Channel_0 ((uint8_t)0x00)
#define ADC_Channel_1 ((uint8_t)0x01)
#define ADC_Channel_2 ((uint8_t)0x02)
#define ADC_Channel_3 ((uint8_t)0x03)
#define ADC_Channel_4 ((uint8_t)0x04)
#define ADC_Channel_5 ((uint8_t)0x05)
#define ADC_Channel_6 ((uint8_t)0x06)
#define ADC_Channel_7 ((uint8_t)0x07)
#define ADC_Channel_8 ((uint8_t)0x08)
#define ADC_Channel_9 ((uint8_t)0x09)
#define ADC_Channel_10 ((uint8_t)0x0A)
#define ADC_Channel_11 ((uint8_t)0x0B)
#define ADC_Channel_12 ((uint8_t)0x0C)
#define ADC_Channel_13 ((uint8_t)0x0D)
#define ADC_Channel_14 ((uint8_t)0x0E)
#define ADC_Channel_15 ((uint8_t)0x0F)
#define ADC_Channel_16 ((uint8_t)0x10)
#define ADC_Channel_17 ((uint8_t)0x11)
#define ADC_SampleTime_55Cycles5 ((uint8_t)0x05)
void Adc_Init(void); //ADCͨµÀ³õʼ»¯
u16 Get_Adc(u8 ch); //»ñµÃij¸öͨµÀÖµ
u16 Get_Adc_Average(u8 ch,u8 times);//µÃµ½Ä³¸öͨµÀ10´Î²ÉÑùµÄƽ¾ùÖµ
void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);
void ADC_RegularChannelConfig2( uint8_t ADC_Channel, uint8_t Rank);
void get_adc_dma(char adc[],char num);
#endif
AD.C
#include "ad.h"
#include "delay.h"
u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度
//初始化ADC1
//这里我们仅以规则通道为例
//我们默认仅开启通道1
void Adc_Init(void)
{
//先初始化IO口
RCC->APB2ENR|=1<<2; //使能PORTA口时钟
GPIOA->CRL&=0XFFFFFFF0;//PA0 anolog输入
GPIOA->CRL&=0XFFFFFF0F;//PA1 anolog输入
GPIOA->CRL&=0XFFFFF0FF;//PA2 anolog输入
GPIOA->CRL&=0XFFFF0FFF;//PA3 anolog输入
RCC->APB2ENR|=1<<9; //ADC1时钟使能
RCC->APB2RSTR|=1<<9; //ADC1复位
RCC->APB2RSTR&=~(1<<9);//复位结束
RCC->CFGR&=~(3<<14); //分频因子清零
//SYSCLK/DIV2=12M ADC时钟设置为12M,ADC最大时钟不能超过14M!
//否则将导致ADC准确度下降!
RCC->CFGR|=2<<14;
ADC1->CR1&=0XF0FFFF; //工作模式清零
ADC1->CR1|=0<<16; //独立工作模式
ADC1->CR1|=(1<<8); //扫描模式
ADC1->CR2|=(1<<1); //连续转换模式
ADC1->CR2&=~(7<<17); //清零启动规则通道转换的外部时间
ADC1->CR2|=7<<17; //软件控制转换
ADC1->CR2|=1<<20; //使用用外部触发(SWSTART)!!! 必须使用一个事件来触发
ADC1->CR2&=~(1<<11); //右对齐
ADC1->CR2 |= 1<<8;//使用DMA模式,这个地方卡了很久
// ADC1->SQR1&=~(0XF<<20);
// ADC1->SQR1|=0<<20; //1个转换在规则序列中 也就是只转换规则序列1
// //设置通道1的采样时间
// ADC1->SMPR2&=~(3*1); //通道1采样时间清空
// ADC1->SMPR2|=7<<(3*1); //通道1 239.5周期,提高采样时间可以提高精确度
// ADC1->CR2|=1<<0; //开启AD转换器
//该位由软件设置以开始校准,并在校准结束时由硬件清除
}
/*
//DMA_CHx:DMA通道CHx ,比如DMA1_Channel1
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
*/
void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC->AHBENR|=1<<0; //开启DMA1时钟
Delay_ms(5); //等待DMA时钟稳定
DMA_CHx->CPAR=cpar; //DMA1 外设地址
DMA_CHx->CMAR=(u32)cmar; //DMA1,存储器地址
DMA1_MEM_LEN=cndtr; //保存DMA传输数据量
DMA_CHx->CNDTR=cndtr; //DMA1,传输数据量
DMA_CHx->CCR=0X00000000; //复位
DMA_CHx->CCR &= ~(1<<4); //从外设读
DMA_CHx->CCR|=1<<5; //循环模式
DMA_CHx->CCR|=0<<6; //外设地址非增量模式
DMA_CHx->CCR|=1<<7; //存储器增量模式
DMA_CHx->CCR|=1<<8; //外设数据宽度为16位
DMA_CHx->CCR|=1<<10; //存储器数据宽度16位
DMA_CHx->CCR|=1<<12; //中等优先级
DMA_CHx->CCR|=0<<14; //非存储器到存储器模式
DMA_CHx->CCR|=1<<0; //开启DMA传输
}
//开启一次DMA传输
//void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
//{
// DMA_CHx->CCR&=~(1<<0); //关闭DMA传输
// DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,传输数据量
// DMA_CHx->CCR|=1<<0; //开启DMA传输
//}
/*只适用最多6个规则序列的转换
最多转换16个,也就是16个序列。转换通道的rank与在数组中的位置一样。
*/
//void get_adc_dma(char adc[],char num)
//{
// //配置通道进行转换
// char i;
// ADC1->SQR1 &= 0;
// ADC1->SQR1 |= ((num-1) << 20);
// for(i=0;i<num;i++)
// {
// ADC1->SQR3 &= ~(0x1F << (i*5));
// ADC1->SQR3 |= (adc[i] << (i*5));
// }
//
// ADC1->CR2 |= 1<<22;
// while(!(ADC1->SR & (1<<1)));
//
// ADC1->SR &= ~(1<<1);
//}
void get_adc_dma(char adc[],char num)
{
char i = 0;
ADC1->SQR1 &= 0;
ADC1->SQR1 |= ((num-1) << 20);//配置要转换的数量
for(i = 0;i < num;i++)
{
ADC_RegularChannelConfig2(adc[i],(i+1));
}
ADC1->CR2|=1<<0;
ADC1->CR2|=1<<3; //使能复位校准
while(ADC1->CR2&1<<3); //等待校准结束
//该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。
ADC1->CR2|=1<<2; //开启AD校准
while(ADC1->CR2&1<<2); //等待校准结束
ADC1->CR2|=1<<22; //启动规则转换通道
}
/*
只用ADC1,先不考虑ADC2、ADC3
ADC_SampleTime统一配置为 55.5 cycles */
void ADC_RegularChannelConfig2( uint8_t ADC_Channel, uint8_t Rank)
{
if(ADC_Channel > ADC_Channel_9)
{
ADC1->SMPR1 |= ((uint32_t)ADC_SampleTime_55Cycles5 << (3 * (ADC_Channel -10)));
}
else/*ADC Channel include in ADC_Channel[0:9] */
{
ADC1->SMPR2 |= ((uint32_t)ADC_SampleTime_55Cycles5 << (3*ADC_Channel));
}
/*接下来配置rank*/
if(Rank < 7)
{
ADC1->SQR3 |= ((uint32_t)ADC_Channel << ((Rank - 1)*5));
}
else if(Rank < 13)
{
ADC1->SQR2 |= ((uint32_t)ADC_Channel << (5*(Rank - 7)));
}
else
{
ADC1->SQR1 |= ((uint32_t)ADC_Channel << (5*(Rank-13)));
}
}
//获得ADC1某个通道的值
//ch:通道值 0~16
//返回值:转换结果
//u16 Get_Adc(u8 ch)
//{
// //设置转换序列
// ADC1->SQR3&=0XFFFFFFE0;//规则序列1 通道ch
// ADC1->SQR3|=ch;
// ADC1->CR2|=1<<22; //启动规则转换通道
// while(!(ADC1->SR&1<<1));//等待转换结束
// return ADC1->DR; //返回adc值
//}
//获取通道ch的转换值,取times次,然后平均
//ch:通道编号
//times:获取次数
//返回值:通道ch的times次转换结果平均值
//u16 Get_Adc_Average(u8 ch,u8 times)
//{
// u32 temp_val=0;
// u8 t;
// for(t=0;t<times;t++)
// {
// temp_val+=Get_Adc(ch);
// Delay_ms(5);
// }
// return temp_val/times;
//}
最后是main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD_Value[4] = {0};
char adc_channel[16]={0,1,2,3};
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Adc_Init(); //AD初始化
MYDMA_Config(DMA1_Channel1,(uint32_t)&ADC1->DR,(uint32_t)AD_Value,4);
get_adc_dma(adc_channel,4);
/*显示静态字符串*/
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
OLED_ShowString(4, 1, "AD3:");
while (1)
{
OLED_ShowNum(1, 5, AD_Value[0], 4); //显示转换结果第0个数据
OLED_ShowNum(2, 5, AD_Value[1], 4); //显示转换结果第1个数据
OLED_ShowNum(3, 5, AD_Value[2], 4); //显示转换结果第2个数据
OLED_ShowNum(4, 5, AD_Value[3], 4); //显示转换结果第3个数据
Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
}