STM32F103 ADC+DMA多通道转换(寄存器实现版)


为了自己记录
借用了江科协的代码和正点原子的代码。。
有个地方弄错了,调试好久,废话不多话,贴代码

目的

写一个基于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,手动增加一些转换的间隔时间
	}
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值