初级会员

- 积分
- 65
- 金钱
- 65
- 注册时间
- 2016-1-4
- 在线时间
- 13 小时
|
1金钱
本帖最后由 cpu12g000 于 2016-1-30 17:59 编辑
本代码的目的是,进行三路通道的扫描,(当然可修改成更多路),
扫一次处理一次,然后软件控制再一次扫描。
看到许多文章,没见到有ADC扫描模式单次转换的代码。
见这里许多弟兄议的热火朝天,俺也来凑个热闹。
经过两天的琢磨试验,搞了这几行代码。经试成功。
如果此篇文章对你有帮助,不妨高抬贵手点个赞,让俺心里安慰一下。呵呵。
闲话少说,上代码:
(这里面头文件及主函数不晒出了,相信都一看下文都明白,
过两天来此一游,如有需要者,我再晒出)
#include "adc.h"
#include "delay.h"
#include "dma.h"
#include "stm32f10x.h"
#include "oled.h"
DMA_InitTypeDef DMA_InitStructure;
#define SEND_BUF_SIZE 3 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
u16 RevBuff[SEND_BUF_SIZE]; //发送数据缓冲区
u8 MyDmaLen = 3;
u16 DMA1_MEM_LEN = 3;//保存DMA每次数据传送的长度
//以上这几行代码写的有点啰嗦,凑合着看吧,不行自已可以精炼一下,我懒得改了。
/DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从外设模式-〉存储器/16位数据宽度/存储器增量模式
//DMA_CHx MA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值
DMA1_MEM_LEN=cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读取数据发送到内存
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道ADC_DMA_Channel所标识的寄存器
}
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE ); //关闭TX DMA1 所指示的通道
//必须先关闭这个通道后,才能修改下面的缓存
DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
//实际上,这里的 DMA1_MEM_LEN 跟DMA初始化结构体中的 ”DMA_InitStructure.DMA_BufferSize = cndtr;"
//是一个变量,下面看看它原始材料中的描述。因为它会在一次传输后减为0,所以要重新装载。
//通道开启后该寄存器变为只读,指示剩余的待传输字节数目。
//寄存器内容在每次DMA传输后递减。
//数据传输结束后,寄存器的内容或者变为0;
//原文“或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时的数值。
//当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。”
DMA_Cmd(DMA_CHx, ENABLE); //使能 TX DMA1 所指示的通道
}
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1,ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 |GPIO_Pin_2|GPIO_Pin_3;//三路AD采集的输入脚?
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //,模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //模数转换工作在扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 3; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
//ADC_DMACmd(ADC1,ENABLE);
//设置指定ADC的规则组通道,一个序列,采样时间
//以下是函数原型
// void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel,
//uint8_t Rank, uint8_t ADC_SampleTime // Rank 为槽的顺序
ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_RegularChannelConfig(ADC1, 2, 2, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_RegularChannelConfig(ADC1, 3, 3, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
//
//应先将这里分配DMA 的设置,最后效准:
//-------------------------------------------------------------
MYDMA_Config(DMA1_Channel1, (u32)&ADC1->DR,(u32)RevBuff,MyDmaLen);
//这里一定要先开启DMA通道
//DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //使能传输完成中断。
//-------------------------------------------------------------
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}
void Enter_Adc(void)
{
MYDMA_Enable(DMA1_Channel1); //此处必须重新设定DMA使能,因为DMA计数器会减到0
//ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
//CR2_EXTTRIG_SWSTART_Set 注意这个值是:500000
//即它同时启动了:SWSTART 和 EXTTRIG
//这也就是为什么被强调:"必须使用一个事件来触发" 的理由:
// 即: ADC1->CR2 = 1 << 2; (SWSTART = 1)
//ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
//while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
//return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
//SWSTART:开始转换规则通道 (Start conversion of regular channels) 位22 由软件设置该位以启动转换,
//转换开始后硬件马上清除此位。(笔者注:这是区别转换与再次转换不同之处)
//以下为原文摘录,主要是为了让弟兄们更加明白:
//如果在EXTSEL[2:0]位中选择了SWSTART为触发事件,该位用于启动一组规则通道的转换,
//0:复位状态; 1:开始转换规则通道。
//ADON:开/关A/D转换器 (A/D converter ON / OFF) 该位由软件设置和清除。当该位为’0’时,写入’1’将把ADC从断电模式下唤醒。
// 当该位为’1’时,写入’1’将启动转换。
//应用程序需注意,在转换器上电至转换开始有一个延迟tSTAB,参见图25。
//0:关闭ADC转换/校准,并进入断电模式; 1:开启ADC并启动转换。
//注:如果在这个寄存器中与ADON一起还有其他位被改变,则转换不被触发。这是为了防止触发错误的转换。
//---------------------------------------------------------------------------------
//这个函数在读取一次三组数据后,可反复调用,进行下次的读取。
//-------------------------------------------------------------------------------
}
void ADC_Display(void)
{
u32 VTemp[3];
if(DMA_GetFlagStatus(DMA1_FLAG_TC1) == SET)//如果传输完毕标志位设置
{
DMA_ClearFlag(DMA1_FLAG_TC1|DMA1_FLAG_HT1|DMA1_FLAG_GL1); //清除传输完成标志位
VTemp[0] = 3300 * RevBuff[0]/4095;
VTemp[1] = 3300 * RevBuff[1]/4095;
VTemp[2] = 3300 * RevBuff[2]/4095;
OLED_ShowNumbFour(8,8,VTemp[0],12,0,1);// 这个地方是我自定义的一个显示四位数的函数。不必深究
OLED_ShowNumbFour(8,24,VTemp[1],12,0,1);
OLED_ShowNumbFour(8,40,VTemp[2],12,0,1);
OLED_Refresh_Gram();
//Get_Adc();
}
//这个地方说明一下:当ADC连续扫描模式+DMA的时候,扫描结束时EOC 不置位,调试中已发现。
//因为什么?因为原文有句话:大意是:当ADC->DR(ADC的结果寄存器被读取后,它会清此位,)
//而英文资料中有句话(中文没有)每次ADC单个通道转换结束后,就会发生一次DMA传输,
//意思是DMA控制器读取了ADC->DR,所以,EOC自然不置位了。
}
void DMA1_Channel1_IRQHandler(void)
{
// @arg DMA1_FLAG_GL1: DMA1 Channel1 global flag.
// @arg DMA1_FLAG_TC1: DMA1 Channel1 transfer complete flag.
//if(DMA_GetFlagStatus(DMA1_FLAG_TC1) == SET)//如果传输完毕标志位设置
//DMA_ClearFlag(DMA1_FLAG_TC1); //清除传输完成标志位
if(DMA_GetITStatus(DMA1_IT_TC1) == SET) //
DMA_ClearITPendingBit(DMA1_IT_TC1); //经查,这两函数跟上面的一样,不知为什么弄两个这种函数
//看库函数中的详尽代码中,以上这两组函数说的是一个东西,不知为啥弄得重复。
//此中断函数我这里没用,有想调用的,可改一下。
}
|
最佳答案
查看完整内容[请看2#楼]
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
//实际上,这里的 DMA1_MEM_LEN 跟DMA初始化结构体中的 ”DMA_InitStructure.DMA_BufferSize = cndtr;"
//是一个变量,下面看看它原始材料中的描述。因为它会在一次传输后减为0,所以要重新装载。
//通道开启后该寄 ...
|