当前位置:首页 > 公众号精选 > wenzi嵌入式软件
[导读]引言 ADC 的功能是将模拟信号采样得到数字信号,而有些时候,我们需要使用到定时采样,比如在计算一个采集的波形的频率的时候,我们需要精确的知道采样频率,也就是 1 s 内采集的点数,这个时候,就需要使用到定时采集。定时采样有如下三种方法: 使用定时器


引言

ADC 的功能是将模拟信号采样得到数字信号,而有些时候,我们需要使用到定时采样,比如在计算一个采集的波形的频率的时候,我们需要精确的知道采样频率,也就是 1 s 内采集的点数,这个时候,就需要使用到定时采集。定时采样有如下三种方法:

  • 使用定时器中断,每隔一段时间进行 ADC 转换,但是这样每次都必须读 ADC 的数据寄存器,非常浪费时间。

  • 把 ADC 设置成连续转换模式,同时对应的 DMA 通道开启循环模式,这样 ADC 就一直在进行数据采集然后通过 DMA 把数据搬运至内存。这样进行处理的话,需要加一个定时中断,用来读取内存中的数据。

  • 使用 ADC 的定时器触发 ADC 转换的功能,然后使用 DMA 进行数据的搬运。这样就只要设置好定时器的触发间隔,就能实现 ADC 定时采样转换的功能,然后使能 DMA 转换完成中断,这样每次转换完就会产生中断。

本文,笔者将采用第三种方法进行 AD 采集,使用 TIM 定时器触发 AD 采集,然后 DMA 搬运至内存。

ADC 简介

首先来看一下 ADC 的框图:

ADC 框图


在本文中,我们使用的是规则通道进行转换,这里要指出的一点是规则通道和注入通道两者的区别,以下是关于两种通道的说明:

  • 规则通道:我们平时使用的就是这个通道,就是规规矩矩的按照我们设定的转换顺序就行转换的通道。

  • 注入通道:注入通道可以理解为是插入,也就是插队的意思,它是一种不安分的通道。它是一种在规则通道转换的时候强行插入要进行转换的一种,它的存在就像是程序中的中断一样,换个角度说,也就是注入通道只有在规则通道存在的情况下才会存在。

说了规则通道和注入通道的区别之后,我们来看我们在本文中所用到的规则通道的触发方式。我们最为常用的一种就是软件触发,即配置到 ADC 之后,就会自动地进行转换,然后去读 ADC 的数据寄存器就可以得到 ad 转换得到的数值。还有一种方法就是外部触发,而外部触发又包括定时器触发和外部 IO 触发,在本文中,我们使用的是定时器触发,通过上述的 ADC 功能框图,我们可以知道 ADC 的定时器触发又有如下几种类型:

  • TIM1_CH1 :定时器 1 的通道 1 的 PWM 触发

  • TIM1_CH2 : 定时器 2 的通道 2 的 PWM 触发

  • TIM1_CH3: 定时器 1 的通道 3 的 PWM 触发

  • TIM2_CH2 : 定时器 2 的通道 2 的 PWM 触发

  • TIM3_TRGO: 定时器 3 触发,TRGO属于内部触发,不需要配置对应的输出IO脚.相当于是TIM3的定时器内部计数一样,只是到了一定时间就触发ADC转换,而这个触发的实现,不依赖IO口的配置.

  • TIM4_CH4 : 定时器 4 的通道 4 的 PWM 触发

定时器配置

在进行了上述简单的介绍之后,我们来具体到代码的细节来看,本文采用的是 TIM4_CH4 进行外部触发 ADC 采样。首先来看 TIM 的配置,代码如下:

void ADC1_External_T4_CC4_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;
    TIM_OCInitTypeDef         TIM_OCInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

    /* Time Base configuration */
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
    TIM_TimeBaseStructure.TIM_Period = 72 - 1;          
    TIM_TimeBaseStructure.TIM_Prescaler = sample_psc;       
    TIM_TimeBaseStructure.TIM_ClockDivision = 0x00;    
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

    /* TIM1 channel1 configuration in PWM mode */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                
    TIM_OCInitStructure.TIM_Pulse = 60
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;         
    TIM_OC4Init(TIM4, &TIM_OCInitStructure);

    TIM_CtrlPWMOutputs(TIM4, ENABLE);
    TIM_Cmd(TIM4, DISABLE);
}

在这里需要注意的是 和 sample_psc 是个变量,而这个变量可以通过调用库函数 TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC) 重新配置 TIM 所产生的 pwm 的频率,详细的原理不在这里进行赘述了,既然都能够改变 TIM 产生的 PWM 的原理,那么也就能够动态地改变 ADC 的采样频率,也就是决定 ADC 在 1 s 中能够采样多少个点,具体的原理在后续指出。还有一个需要注意的地方是 TIM_Cmd(TIM4,DISABLE),这里配置的是禁止 TIM 定时器使能,因为还有 ADC 和 DMA 还没有进行配置,因此,我们需要在 ADC 和 DMA 都配置好之后,再将 TIM4 进行使能。

DMA 配置

因为笔者所涉及到的 ADC 的具体应用是这样的,也就是通过定时器触发 ADC 采集,然后采集一定数量的点数之后,在这里笔者每个 ADC 的通道是采集了 256 个点,然后对这 256 个点进行处理,处理完毕之后,再以一定时间间隔再采集 256 个点,周而复始地进行采集和处理。并且,这里需要的是同时采集 2 个通道的数据,每个通道采集 256 个点,也就是说,我们一次性处理的是 256 * 2 = 512 个点的数据,采集完成之后,再通过 DMA 将数据其搬运至内存,因此,也就有了如下所示的 DMA 配置:

static void ADC1_DMA1_Init(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* DMA1 Channel1 Configuration ----------------------------------------------*/
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = ADC_BUFF_LEN*2;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    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_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    /* Enable DMA1 channel1 */
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

代码比较直观,都是一些相关的配置,这里所要指出的一点是在第五行配置了中断服务函数 DMA1_Channel1_IRQn,具体的思路就是当采集的点数满足设定的点数时,就进入中断服务函数进行处理,在这里需要注意的是我们是从 ADC 外设将数据搬运至内存,所以DMA外设的地址是 ADC1 数据寄存器的地址,可以使用宏定义的方式定义如下:

#define ADC1_DR_Address    ((uint32_t)0x4001244C)

也可以直接取地址的方式设置,设置方式如下所示:

DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 ) ( & ( ADC_x->DR ) );

设置好外设的地址之后,我们就需要设置内存的地址,在这里,因为我们要采集两个通道的数据,并且每个通道要采集 256 个点的数据,所以在这里定义了一个如下所示的二维数组:

uint16_t ADC_ConvertedValue[ADC_BUFF_LEN][2] = {0};

上述中的 ADC_BUFF_LEN 就是一个通道要采集的点数,也就是 256 个,2所代表的就是有两个通道。在这里需要稍微思考的一下是二维数组的定义方式,为什么定义成的是 256 行 2 列 的二维数组,而不是 2 行 256 列的二维数组,我们来看一下 256 行 2 列的数组的布局如下:

二维数组内存分布


根据二维数组的大小也解释了 DMA 的 Buffer_size 是 ADC_BUFF_LEN * 2 ,同时,由于在下面设置了 内存地址是递增的,而又有两个通道,那么他的转换顺序是这样的,也就是先转换通道 1 的值存入数组,然后再转换通道 2 的数据存入数组,然后,以一定时间间隔地转换 512 次,然后发生 DMA 中断,这样也就能够说明数组为什么是定义成 256 行 2 列了。

ADC 配置

在配置了定时器和 DMA 之后,我们接下来来进行 ADC 的配置,上文中,我们配置的是使用 TIM4 的 4 通道产生 PWM 来触发 ADC 进行采集,然后设置了 DMA 来进行数据的搬运,因此, ADC 模块的配置如下所示:

void ADC_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_InitTypeDef ADC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    /* 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_T4_CC4;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 2;
    ADC_Init(ADC1, &ADC_InitStructure);

    RCC_ADCCLKConfig(RCC_PCLK2_Div6); 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_239Cycles5);  

    ADC_Cmd(ADC1, ENABLE);

    //外部触发
    ADC_ExternalTrigConvCmd(ADC1, ENABLE);

    //使用DMA
    ADC_DMACmd(ADC1, ENABLE);

    //校准ADC
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));

    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));  

}

配置过程比较简单,没有什么逻辑性可言,不在这里进行赘述,这里需要指出的一点是因为我们设置的是 2 个通道的采集,所以,在这里应该使能 ADC 的扫描模式,另一方面,我们采用的是 TIM 产生 pwm 触发 adc 进行采集,所以要禁止 ADC 的连续转换模式,这就是两个需要注意的地方。

DMA 中断服务函数

在前文我们说了,我们通过 pwm 触发 ADC 采集,当采集了规定的点数之后,就会产生 DMA 中断,然后在 DMA 中断里面去处理数据,但是由于中断服务函数的要求是执行时间尽可能短,所以,我们可以在中断服务函数里置位数据采集完成标志位的方式来使得主程序进行数据处理,程序代码如下所示:

void ADC1_DMA1_IT_Hander(void)
{    
    if (DMA_GetFlagStatus(DMA1_FLAG_TC1))
    {
        DMA_ClearITPendingBit(DMA1_FLAG_TC1);
        //rt_sem_release(adc_complete_sem);
        adc_complete_flag = 1;
    }
}

上述代码中,被注释掉的部分是释放信号量,这个是使用 RTOS 是用来同步线程的一个操作,其功能与裸机的标志位是相同的。

总结

上述便是本次分享的内容,其实现的一个功能便是使用 PWM 触发 ADC 多通道采集,并使用 DMA 进行搬运,通过这样子就可以精确地控制 ADC 的采样频率,也就是控制 1 s 钟可以采集多少个点。最后,而这个采样频率就是 pwm 的频率,但是为了更加精确的计算其真实的采样频率还应该加上 ADC 通道的转换一个数据的转换时间,这样才是最为精确的采样频率。在下一篇文章中,笔者将继续介绍基于这篇文章的应用,也就是根据采样得到的点,计算波形的频谱,计算波形的频率。

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭