历史上的今天
返回首页

历史上的今天

今天是:2025年02月19日(星期三)

正在发生

2020年02月19日 | stm32l151 ADC通过DMA通道定时采样电池电量

2020-02-19 来源:eefocus

  最近在使用stm32l151开发一个项目,我的项目需求是ADC采集电池电量,通过DMA通道传送出来。然而我并不是得到了电池电量数据后就立马连续输出,而是通过tim4定时器每1s访问一次采样得到的电池数据,并显示出来。本来网上关于stm32通过adc通道采集电池电量的代码很多,但要找到和我的需求一样的,还真没有。于是在借鉴其他人代码的基础上,根据我的特殊需求,写了一份这样功能的代码。


  因为我的需求涉及到了tim4定时器,adc和dma,所以在最终配置的时候也分为几个部分:

  1.首先是定时器tim4。

   a)初始化


void vTim4_Init(void)

{

 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

 

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);


/* Time base configuration */

TIM_TimeBaseStructure.TIM_Period = TIMER4_PERIOD_TIMING;

TIM_TimeBaseStructure.TIM_Prescaler = u32Tim4PrescalerValue;


TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;


TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

/* TIM IT enable */

TIM_ITConfig(TIM4, TIM_IT_Update |TIM_IT_Trigger, ENABLE); 


  /* TIM14 disable */

TIM_Cmd(TIM4, DISABLE);

}


   b)NVIC中断设置


/* Enable the TIMER4 Interrupt */

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(&NVIC_InitStructure);

}


   c)Tim4中断函数书写,我这里是通过给任务一个信号量longtimerTaskSemaphoreHandler,唤醒获取电量的任务来获取电量数据并显示。


void vTimer4_Interrupt(void)

{

BaseType_t xHigherPriorityTaskWoken;

if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)

{

xSemaphoreGiveFromISR(longtimerTaskSemaphoreHandler,&xHigherPriorityTaskWoken);

if(xHigherPriorityTaskWoken == pdTRUE)

portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

TIM_ClearITPendingBit(TIM4, TIM_IT_Update);

}

}


  2.其次是adc。

   a)adc的GPIO口初始化


void ADC_GPIO_INIT()

{

GPIO_InitTypeDef GPIO_InitStructure;


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);  //打开对应GPIO的时钟

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_Init(GPIOA, &GPIO_InitStructure);

}


   b)adc通道的初始化设置


void ADC_INIT()

{

ADC_InitTypeDef ADC_InitStructure;


RCC_HSICmd(ENABLE);                    //开启HSI时钟,非常重要,stm32l151的时钟由HSI提供

while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);      //使能ADC1时钟

ADC_InitStructure.ADC_ScanConvMode             = DISABLE;        //关闭扫描模式,因为多ADC通道同时使用时才打开

ADC_InitStructure.ADC_ContinuousConvMode       = ENABLE;        //连续转换模式,因为我需要连续取十次数据

ADC_InitStructure.ADC_ExternalTrigConvEdge     = ADC_ExternalTrigConvEdge_None;        //不适用外部触发转换

ADC_InitStructure.ADC_DataAlign                = ADC_DataAlign_Right;                 //采集数据右对齐

ADC_InitStructure.ADC_Resolution               = ADC_Resolution_12b;       //分辨率是12位

ADC_InitStructure.ADC_NbrOfConversion          = 1;                  //要转换的通道数目1

ADC_Init(ADC1, &ADC_InitStructure);


ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_48Cycles);     //设置adc1的通道0的转换周期为48个采样周期

ADC_DMACmd(ADC1, ENABLE);                                                 //启动DMA搬运ADC数值

ADC_Cmd(ADC1, ENABLE);                                                    //打开ADC

while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET);    //等待ADC1转换完成,必须返回值是SET  

ADC_SoftwareStartConv(ADC1);                            //打开软件触发

}


  3.dma的设置。

   a)dma初始化设置


void DMA_CONFIG()

{

DMA_InitTypeDef DMA_InitStructure;


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                       //开启DMA时钟

DMA_DeInit(DMA1_Channel1);                                             //DMA复位

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));             //ADC地址

DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)&ADC_ConvertedValue;     //存储地址

DMA_InitStructure.DMA_DIR                        = DMA_DIR_PeripheralSRC;     //DMA传输方向为设备到内存

DMA_InitStructure.DMA_BufferSize               = 10;                   //存储大小,所以前面的ADC我设置的是连续转换

DMA_InitStructure.DMA_PeripheralInc          = DMA_PeripheralInc_Disable;                //外存地址固定                 

DMA_InitStructure.DMA_MemoryInc            = DMA_MemoryInc_Enable;                 //内存地址递增,这样从ADC转换过来的数据才能依次传到目标地址上,否则的话就是固定写在一个地址上,并且前面的数据把后面的覆盖了                                                                             

DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;     //每次发送是半字,即16位

DMA_InitStructure.DMA_MemoryDataSize    = DMA_MemoryDataSize_HalfWord;           //每次存储数据单位为半字,即16位

DMA_InitStructure.DMA_Mode              = DMA_Mode_Circular;                     //循环AD

DMA_InitStructure.DMA_Priority                  = DMA_Priority_High;      //DMA优先级高

DMA_InitStructure.DMA_M2M                      = DMA_M2M_Disable;         //DMA禁止内存到内存

DMA_Init(DMA1_Channel1, &DMA_InitStructure);                              //DMA初始化

DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);                           //使能DMA发送完中断

DMA_ClearITPendingBit(DMA1_IT_TC1);                                       //清除中断位

DMA_Cmd(DMA1_Channel1, ENABLE);                                           //启动DMA通道1

}


   b)dma的中断设置


void DMA_CONFIG()

{

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}


   c)dma中断函数书写。


void DMA1_Channel1_IRQHandler(void)

{  

u32 i;

u32 After_Buff=0;

 

    if (DMA_GetITStatus(DMA_IT_TC) != RESET)

    {

      DMA_ClearITPendingBit(DMA1_IT_TC1);        //清除DMA中断标志位            

ADC_DMACmd(ADC1, DISABLE);                //停止DMA搬运ADC数值

ADC_Cmd(ADC1,DISABLE);                    //停止ADC转换,我是通过Tim4定时器产生的中断来开关DMA和ADC。

for(i = 0;i < buff_size;i++)

{

After_Buff = After_Buff+ADC_ConvertedValue[i];

After_Filter=After_Buff/10;   //强制转换后电量显示正常

After_Buff=0;

    }

   

}



    最后回到mian()函数部分了,通过Tim4定时触发读取电池电量的任务,读取并显示。


void AdcTransfer(void)

{

u32 powervalue;

ADC_DMACmd(ADC1, ENABLE);                 //开启DMA搬运数据

ADC_Cmd(ADC1, ENABLE);                    //开启ADC转换 

while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET) {};     //如果ADC允许使用

  ADC_SoftwareStartConv(ADC1);                                 //开启ADC

  while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));                 //如果ADC转换结束  

powervalue = (uint16_t)((After_Filter*3300*3)>>12);

}


至此,读取ADC电量步骤圆满完成!

推荐阅读

史海拾趣

亿佰特(EBYTE)公司的发展小趣事

亿佰特注重团队建设和人才培养。公司吸引了一批具有丰富经验和专业技能的人才加入,形成了一支高效、专业的团队。公司还注重员工的培训和发展,为员工提供广阔的晋升空间和职业发展机会。正是这支优秀的团队,为亿佰特的发展提供了源源不断的动力。

Aavid Niagra公司的发展小趣事

为了拓展市场份额,Aavid Niagra公司开始实施全球化战略布局。公司先后在多个国家和地区设立了生产基地和研发中心,以便更好地服务当地客户。同时,公司还积极参与国际电子行业的交流与合作,不断提升自身的国际影响力。全球化战略的实施为公司带来了更多的发展机遇,也使其在全球电子散热市场上占据了重要地位。

Amecon Magnetics公司的发展小趣事

Amecon Magnetics公司成立于XXXX年,初始阶段主要专注于磁性材料的研发与生产。在创立初期,公司面临着资金短缺、技术挑战以及市场竞争激烈等多重困难。然而,创始人凭借对磁性材料领域的深刻理解和坚定信念,带领团队不断突破技术难题,逐步开发出具有竞争力的产品。

Galaxy Semi-Conductor Co Ltd公司的发展小趣事

山西飞虹光电科技集团有限公司原以煤炭、洗煤和焦化等生产为主,但在响应山西省委、省政府转型跨越发展的号召下,公司于2010年向高科技产业基地转型。在临汾·甘亭新型工业园区投资16.5亿元建设了“山西陆合飞虹光电科技园”,专注于研发和生产化合物半导体外延片、芯片以及大功率半导体激光器等高科技产品。飞虹光电科技集团通过技术创新,在大功率激光外延片、芯片及LED红、蓝、紫光材料等方面填补了山西省的多项空白,并荣获多项国内外专利及科技奖项。

Bytes公司的发展小趣事

Bytes公司自成立以来,始终坚持以技术创新为核心竞争力。公司早期便投入大量研发资源,开发出一款具有划时代意义的电子产品,迅速在市场上占据一席之地。随着技术的不断进步,Bytes公司不断推出更新换代的产品,满足消费者日益增长的需求。同时,公司还积极与高校、科研机构合作,共同研发新技术,为公司的持续发展提供源源不断的动力。

General Instrument Corp公司的发展小趣事
ML2281系列变换器的转换时间低至6µs,适用于需要快速数据采集的系统。

问答坊 | AI 解惑

逻辑综合器的故事

FPGA设计流程中,逻辑综合的过程非常重要。 由于HDL代码的多样性,逻辑综合的质量对最终设计性能的影响非常大。       在.18微米到.15微米的时代,FPGA的性能很大程度上取决于门延迟。根据我的经验,微米时代,FPGA内部延迟 ...…

查看全部问答>

彩转黑摄像机的“高感度”和“低照度”

关键字:彩转黑摄像机     近年来,随着芯片技术的更新换代,彩转黑摄像机的整体性能有了很大的提高,“高感度,低照度”是各大厂家力推的产品特色,可以说是对弱光环境的一次集体的叫板。     对于安防监控来说,对图像品质 ...…

查看全部问答>

到底掌握多少单片机才算懂?

单片机种类繁多,作为一个开发者。到底需要掌握多少种呢? 掌握多少种才算懂?…

查看全部问答>

炉温检测

单片机  pid算法 温度检测原理图…

查看全部问答>

请教:如何控制2.4G射频识别角度??

在做2.4G射频识别时,发现有源射频识别系统的角度很难控制,尤其当标签距离识别设备小于10米时,角度基本上都是360。 在网上看到有些高人说,射频角度可以控制在15°...有谁有这方面的经验没有?如何有效控制有源射频的识别角度。 我的想法是这样 ...…

查看全部问答>

如何调整核心内存的大小

向大家请教个问题:默认情况下,4G的内存,核心内存和用户态内存分别是2G,现在我写了一个驱动,需要更多的核心内存,因此想把核心内存调整的大些,应该如何做到呢?   另外,我还有个机器是8G内存,跑wxp64位系统,也想调整核心内存更 ...…

查看全部问答>

手机开发USB口问题

将手机和PC机用USB相连: 当先将未开机的手机用USB线连到PC上,再打开手机时,从PC上向手机发送AT指令,手机能向PC返回OK;可是当先将手机开机,再将手机用USB线连接到PC机时,从PC上向手机发送AT指令,手机却不能向PC返回任何东西。不知以上是什么 ...…

查看全部问答>

ioctl返回的地址问题?初学者,高人指点。

请问ioctl返回的地址是虚拟地址还是物理地址? 小弟在做dm6446上的resize,用ioctl返回的resize驱动中申请的buffer的地址是在用户空间是不能用的? 大家指教。 ioctl返回的地址问题?初学者,高人指点。…

查看全部问答>

字符串操作的问题

我需要拆分一个字符串,没有找到好的方法。 比如: cstring FullName = \"Storage Card\\\\media\\\\test.mp3\"; cstring strDir = \"\"; cstring strName = \"\"; 我的最终目的是 strDir = \"Storage Card\\\\media\\\\\"; strName = \"tes ...…

查看全部问答>

data verification failed at address 0x0 ?

本帖最后由 dontium 于 2015-1-23 12:45 编辑 仿真器连接正常,我用了光盘里的一个例子,编译好后下载.out程序时出现 data verification failed at address 0x0, please verify target memoty and memory map 以上错误,请问这是什么原因,我用的 ...…

查看全部问答>