ADC

《内核学习营》+水一方+项目中应用的ADC实现电压采集

发布于 2018-09-13 16:05:17
    本帖最后由 吉帅虎 于 2018-9-13 16:05 编辑


通过参考网友们关于RTT中ADC外设的整理,完成了在STM32F103VCT6上的应用。不过我依然对RTT的外设有一些疑问,我也专门针对这些疑问发了帖子,希望能得到一些解答。疑问贴在这里https://fastadmin.rt-thread.org/ask/question/8007.html
不过除去这些疑问。我别人的驱动的基础上也完成了自己的ADC移植。参考的别人的代码是这个下载附件[driver_adc_example.zip],在此感谢原作者!
*********************************************************************************************
硬件:自用STM32F103VCT6板卡

实验目的:实现多路ADC信号采集。
扩展功能:1、使用env配置对应的AD通道(暂未完成) 2、使用env配置是否使用ADC的DMA功能(暂未完成)
软件平台:env_released_1.0.0 ,mdk5.25
*********************************************************************************************

ADC外设的文件结构如下,一共有4个文件。其中_adc.c和drv_adc.h是与硬件无关的代码,rv_adc.c是与硬件相关的驱动部分;test_adc.c很明显就是测试代码了,我没有用这一部分代码,自己在项目的应用中新建了一个文件。
TIM图片20180913154220.png


按照如下文件结构放置上述三个文件,drv_adc.h跟drv_adc.c放到了一个文件夹下。不知道我对这个文件结构的理解有没有问题。明白的大侠们可以给指点一下。
接下来就是修改与硬件相关的代码部分。在drv_adc.c文件内添加如下代码

//初始化ADC
//ch: ADC_channels
//通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
void ADC1_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;

/********************************************************************************/
//DMA配置
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能MDA1时钟
/* DMA channel1 configuration */
DMA_DeInit(DMA1_Channel1); //指定DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//设置DMA外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue; //设置DMA内存地址,ADC转换结果直接放入该地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设为设置为数据传输的来源
DMA_InitStructure.DMA_BufferSize = 14; //DMA缓冲区设置为12;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道 x拥有高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

/* Enable DMA channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道

//ADC配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/* ADC1 configuration */
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //使用独立模式,扫描模式
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_InitStructure.ADC_NbrOfChannel = 14; // 只有12个转换通道
ADC_Init(ADC1, &ADC_InitStructure);

/* ADC1 regular channel11 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 13, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 14, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 11, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 12, ADC_SampleTime_55Cycles5); //通道1采样周期55.5个时钟周期


/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE); //使能ADC的DMA

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE); //使能ADC1

/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));

/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));

/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //开始一次转换

}


rt_err_t convert(struct rt_device *device, int channel, int *value)
{
rt_err_t result = RT_EOK;

if(channel > (ADC_CHANNEL_MAX - 1))
{
result = -RT_EIO;
goto _exit;
}

//*value = (channel << 8) | (rand() % 0xFF); /* ch=4, return: 0x4xx */
*value = *((Uint16 *)(device->user_data )+channel);

_exit:
return result;
}
这些代码是针对我自己的应用配置的,一共需要转换14个通道,使用DMA方式。其中通道和DMA的配置在通道序号上可能有些错误,由于这个不是原则性错误,暂时没有修改。使用的时候根据实际硬件修改就可以了。有人想参考的话需要注意一下。
ADC的配置按照裸机的方式配置的。一个是IO配置,一个的ADC的参数配置,包括DMA配置。使用DMA的时候,我自己定义了一个全局数组,存放DMA传输的数据。(在操作系统中使用全局变量有没有不合适的地方也请大侠们指点一下。)
rt_err_t convert(struct rt_device *device, int channel, int *value)函数是读取数据的最终执行者。参考文件中返回的是一个随机数,我一开始没有注意看,受到的数据总是不对。修改成*value = *((Uint16 *)(device->user_data )+channel);就可以了。
接下来修改修改的是rt_hw_adc_init()函数,代码如下
int rt_hw_adc_init(void)
{
int ret = RT_EOK;

/* add ADC initial. */
ADC1_GPIO_Config();
ADC1_Config();

//ADC_ConvertedValue ret = rt_device_adc_create(RT_ADC_NAME, &adc_ops, RT_NULL);
ret = rt_device_adc_create(RT_ADC_NAME, &adc_ops, &ADC_ConvertedValue);

return ret;
}
INIT_DEVICE_EXPORT(rt_hw_adc_init);
初始化部分除了初始化模拟输入的IO和ADC参数配置之外,就是创建ADC外设。注意用户数据指针的是存放DMA数据的数组。这样在rt_err_t convert(struct rt_device *device, int channel, int *value)函数里就可以通过*value = *((Uint16 *)(device->user_data )+channel);这样的操作来传递数据了。
int rt_hw_adc_init(void)需要在board.c初始化部分调用,这个大家应该都明白,就不贴代码了
接下来就是应用部分的代码,由于时间比较仓促。只做了一下数据的验证,没有最终按我的项目修改ADC的计算,核心代码如下,代码也是参考的学习营里的外设代码和网友的代码实现的。操作过程分为查找外设。打开外设。读取外设三部分。每一步都可以加错误提示。
 while(1)
{
rt_uint8_t i;
rt_uint16_t val=0;

dev = rt_device_find(RT_ADC_NAME);
if(!dev)
{
rt_kprintf("not found!\n");
}

result = rt_device_open(dev, RT_DEVICE_FLAG_RDONLY);
if(result != RT_EOK)
{
rt_kprintf("open faild! \n");
}

if(rt_device_read(dev,6,&temp,sizeof(temp))!=sizeof(temp))
{
rt_kprintf("channel %d: faild! \n", 1);
}

rt_kprintf("ADC Vaule is:%d \n", temp);
// val += temp;
// rt_thread_delay(1); /* #define RT_TICK_PER_SECOND 1000 */


rt_thread_delay(RT_TICK_PER_SECOND);
}


编译后执行效果如下:

ADC是输入是通过电位器调整实现的,暂时没有进行数据处理,直接显示的是ADC转换之后的结果。
TIM图片20180913160307.png
ADC外设部分虽然做出来了。依然有一些疑惑。扩展的功能还没有完成。但是最新实在是有点儿忙。先完成其他外设的实验再来完成 此实验的扩展功能

查看更多

关注者
0
被浏览
2k
3 个回答
mreasonlee
mreasonlee 2019-04-13
DMA转换不是直接赋值给地址吗?我还没搞明白那个ADC框架怎么去兼容DMA模式和普通模式
mreasonlee
mreasonlee 2019-04-16
谢谢 你的资料很有用,给我很大帮助
mreasonlee
mreasonlee 2019-04-16
另外还有一篇很好的 ADC多通道 DMA的资料,详情以下链接,给有需要的人参考:

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览