基于HC32130F8UA的 4-20maADC采样

HC32130F8UA简介

        华大32内部集成了一个 12 位高精度、高转换速率的逐次逼近型模数转换器(SAR ADC)模块。

其优势在于低功耗,价格低。 

      缺点就是开发环境有点差,而且debug老是有一些莫名的问题,像循环喂狗时老是卡住,而且demo有些写的是真不怎么样(后面发现是有好几个版本),有些与标准库根本对不上。

以下是此次项目的一个简单分析。

一,4-20ma采集原理分析

首先什么是4-20ma?

      4-20mA. DC(1-5V.DC)信号制是国际电工委员会( IEC )过程控制系统采用的模拟信号传输标准。我国也采用这一国际标准信号制,仪表传输信号采用4-20mA.DC,接收信号采用1-5V.DC,即采用电流传输、电压接收的信号系统。

转换原理

     单片机内置ADC采样是无法直接读取电流的,一般是需要将电流值转换为电压值读取。这时你应该想到伟大的欧姆定律,只需要使用一个电阻(以下称为采样电阻)就能将电流值转换成电压值。

二,电路连接

      针对4-20mA电流信号的采集,常规的方案是温漂小的精密电阻(≤1%)将电流转换成电压进行测量,还有更优化方案是采用精密电阻+运放来实现。比如4-20mA的电流使用150Ω的高精度电阻进行I-V转换为0.6-3V,该电压可以直接接到ADC上进行采集。

     采样电阻的选择,主要是看你参考电压的选择,转换的最大电压应当小于且接近参考电压,否则转换电压超过参考电压时,你只能采到参考电压那个值。为了追求稳定,我选取的是单片机内部的2.5V参考电压,故采样电阻选择120R,转换电压范围为0.48-2.4V。

R70采样电阻——薄膜电阻 120Ω ±0.1% 100mW 

R71与C69构成低通滤波电路,【注】R71不应选取过大,否则会影响电流-电压的转换值。

D21稳压二极管,保护单片机。

三,程序设计

GPIO_ADC采样口 IN配置

将PB01引脚配置为模拟输入引脚,读取电压信号。

void adc_gpio_config(void)
{
    stc_gpio_cfg_t stcAdcAN0Port;  
  
  	Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);	//开启ADC/BGR GPIO外设时钟

	  Gpio_SetAnalogMode(GpioPortB, GpioPin1);   
  
    ///打开GPIO外设时钟门控
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    
    ///端口方向配置-输入
    stcAdcAN0Port.enDir = GpioDirIn;
    ///高驱动
    stcAdcAN0Port.enDrv = GpioDrvH;
    ///开启上拉电阻
    stcAdcAN0Port.enPu = GpioPuEnable;
    ///关闭下拉电阻
    stcAdcAN0Port.enPd = GpioPdDisable;
    ///开楼输出关闭
    stcAdcAN0Port.enOD = GpioOdDisable;

    ///初始化
    Gpio_Init(GpioPortB, GpioPin1, &stcAdcAN0Port);
}

ADC单次采样配置

      其实华大的ADC有连续采样模式,但很鸡肋,我记得好像运行10次左右就停了,要再次进行软件,不如单次设条件循环。另因我只采集单路ADC,故没选用DMA方式,华大有个寄存区来存放采集结果,但如果是多路或者要保存结果还是建议选取DMA方式采集和STM32类似。

// ADC模块初始化
static void adc_config(void)
{
	stc_adc_cfg_t stcAdcCfg;
  
//  stc_adc_sqr_cfg_t stcAdcSqrCfg;
  
	DDL_ZERO_STRUCT(stcAdcCfg);
	
	//开启ADC/BGR时钟
    Adc_Enable();
	Sysctrl_SetPeripheralGate(SysctrlPeripheralAdcBgr, TRUE);
    
    M0P_BGR->CR_f.BGR_EN = 0x1u;                 //BGR使能
    delay100us(1);
	
	// ADC ³õʼ»¯ÅäÖÃ
	stcAdcCfg.enAdcMode         = AdcSglMode;		        // 采样模式-单次
	stcAdcCfg.enAdcClkDiv       = AdcMskClkDiv8;            // 时钟选择
	stcAdcCfg.enAdcSampCycleSel = AdcMskSampCycle12Clk;     // 采样周期数-12
	stcAdcCfg.enAdcRefVolSel    = AdcMskRefVolSelInBgr2p5;  // 参考电压选择-内部2.5V
	stcAdcCfg.enAdcOpBuf        = AdcMskBufEnable;         	// 输入信号放大器控制使能
	stcAdcCfg.enInRef           = AdcMskInRefEnable;        // 内部参考电压使能 
	stcAdcCfg.enAdcAlign        = AdcAlignRight;            // 转换结果对齐方式-右
	
	Adc_Init(&stcAdcCfg);                                   //初始化
}


// ADC 单次采样配置
void get_apd_adc(void)
{	

    Adc_CfgSglChannel(AdcExInputCH9);//单次转换通道选择

    // 
    Adc_SGL_Start(); 
   

    ADC_FLAG = ADC_WAITING;
    while(ADC_FLAG==ADC_WAITING)
    {
      if(TRUE == Adc_GetIrqStatus(AdcMskIrqSgl))
      {
         ///< 清楚中断标志位
         Adc_ClrIrqStatus(AdcMskIrqSgl);       
         u32AdcRestult0   =  Adc_GetSglResult();//获取采样值
         
         // 转换为对应4-20ma电流值,这个系数还是你们自己算下
         Vapd_sense = ((float)u32AdcRestult0*0.005032213);


         ADC_FLAG = ADC_COMPLETED;
         
         ///< ADC 单次转化停止
         // Adc_SGL_Stop();                          
      }	
        
    }

}

因为我是采集电流信号来进行阀控,下面是我阀控IO口配置,以及对应采样循环

GPIO_阀门控制 OUT配置

PA09与PB06是控制电机1驱动芯片的引脚,当全部置1时电机刹停

void GPIO_Config_out(void)
{	
    stc_gpio_cfg_t pstcGpioCfg;
    
    ///´开启 GPIO外设时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    
    /// 端口方向配置-输出
    pstcGpioCfg.enDir = GpioDirOut;
    ///< 高驱动能力
    pstcGpioCfg.enDrv = GpioDrvH;
    ///关闭上拉电阻
    pstcGpioCfg.enPu = GpioPuDisable;
    ///关闭下拉电阻
    pstcGpioCfg.enPd = GpioPdDisable;
    ///开漏输出关闭
    pstcGpioCfg.enOD = GpioOdDisable;

    ///< 电机驱动引脚初始化
    Gpio_Init(GpioPortB, GpioPin6, &pstcGpioCfg);
	Gpio_Init(GpioPortA, GpioPin9, &pstcGpioCfg); 
}

ADC循环采样

这段程序放在控制阀门的app.c中方便读取阀门开度

time1-采样时间,如果超过采样时间阀门反馈还未达到指定开度就可能出了问题,采样停止电机刹停

    int time1 = 0;
    user_adc_init();
    while(Vapd_sense<current&&time1<100)   //current阀门开度对应的电流值
     {
          
     get_apd_adc();
          
     if(Vapd_sense t>=current||time1>=100) 
      {          
        GPIO_Config_out();
        Gpio_SetIO(GpioPortB, GpioPin6); //电机驱动引脚拉高,电机刹停
        Gpio_SetIO(GpioPortA, GpioPin9); //
        break;
      }
     time1++;
	 delay1ms(50);
//   BSP_DelayMsWithDog(10);
    }

完整程序

user_adc.c

user_adc.c

#include "bgr.h"
#include "gpio.h"
#include "hc32l13x.h"
#include "adc.h"
#include "app_sensor.h"
#include "ddl.h"
#include "bsp.h"
 
uint8_t ADC_UNIT = ERROR_UNIT;
uint8_t ADC_FLAG = ADC_WAITING;
 
uint32_t u32AdcRestult0;
uint32_t u32AdcRestultTemperature;
float Vapd_sense = 0;

void GPIO_Config_out(void)
{
    stc_gpio_cfg_t pstcGpioCfg;
    
    ///´开启 GPIO外设时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    
    /// 端口方向配置-输出
    pstcGpioCfg.enDir = GpioDirOut;
    ///< 高驱动能力
    pstcGpioCfg.enDrv = GpioDrvH;
    ///关闭上拉电阻
    pstcGpioCfg.enPu = GpioPuDisable;
    ///关闭下拉电阻
    pstcGpioCfg.enPd = GpioPdDisable;
    ///开漏输出关闭
    pstcGpioCfg.enOD = GpioOdDisable;

    ///< 电机驱动引脚初始化¯
    Gpio_Init(GpioPortB, GpioPin6, &pstcGpioCfg);
 Gpio_Init(GpioPortA, GpioPin9, &pstcGpioCfg);
 }

void adc_gpio_config(void)
{
    stc_gpio_cfg_t stcAdcAN0Port;  
  
  	Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);	//开启ADC/BGR GPIO外设时钟
 
	  Gpio_SetAnalogMode(GpioPortB, GpioPin1);   
  
    ///打开GPIO外设时钟门控
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    
    ///端口方向配置-输入
    stcAdcAN0Port.enDir = GpioDirIn;
    ///高驱动
    stcAdcAN0Port.enDrv = GpioDrvH;
    ///开启上拉电阻
    stcAdcAN0Port.enPu = GpioPuEnable;
    ///关闭下拉电阻
    stcAdcAN0Port.enPd = GpioPdDisable;
    ///开楼输出关闭
    stcAdcAN0Port.enOD = GpioOdDisable;
 
    ///初始化
    Gpio_Init(GpioPortB, GpioPin1, &stcAdcAN0Port);
}

// ADC模块初始化
static void adc_config(void)
{
	stc_adc_cfg_t stcAdcCfg;
  
//  stc_adc_sqr_cfg_t stcAdcSqrCfg;
  
	DDL_ZERO_STRUCT(stcAdcCfg);
	
	//开启ADC/BGR时钟
    Adc_Enable();
	Sysctrl_SetPeripheralGate(SysctrlPeripheralAdcBgr, TRUE);
    
    M0P_BGR->CR_f.BGR_EN = 0x1u;                 //BGR使能
    delay100us(1);
	
	// ADC ³õʼ»¯ÅäÖÃ
	stcAdcCfg.enAdcMode         = AdcSglMode;		        // 采样模式-单次
	stcAdcCfg.enAdcClkDiv       = AdcMskClkDiv8;            // 时钟选择
	stcAdcCfg.enAdcSampCycleSel = AdcMskSampCycle12Clk;     // 采样周期数-12
	stcAdcCfg.enAdcRefVolSel    = AdcMskRefVolSelInBgr2p5;  // 参考电压选择-内部2.5V
	stcAdcCfg.enAdcOpBuf       = AdcMskBufEnable;         	// 输入信号放大器控制使能
	stcAdcCfg.enInRef          = AdcMskInRefEnable;        // 内部参考电压使能 
	stcAdcCfg.enAdcAlign        = AdcAlignRight;            // 转换结果对齐方式-右
	
	Adc_Init(&stcAdcCfg);                                   //初始化
}
 
 
// ADC 单次采样配置
void get_apd_adc(void)
{	
 
    Adc_CfgSglChannel(AdcExInputCH9);//单次转换通道选择
 
    // 
    Adc_SGL_Start(); 
   
 
    ADC_FLAG = ADC_WAITING;
    while(ADC_FLAG==ADC_WAITING)
    {
      if(TRUE == Adc_GetIrqStatus(AdcMskIrqSgl))
      {
         ///< 清楚中断标志位
         Adc_ClrIrqStatus(AdcMskIrqSgl);       
         u32AdcRestult0   =  Adc_GetSglResult();//获取采样值
         
         // 转换为对应4-20ma电流值,这个系数还是根据不同的设备重新算下
         Vapd_sense = ((float)u32AdcRestult0*0.005032213);
         ADC_FLAG = ADC_COMPLETED;
         
         ///< ADC 单次转化停止
         // Adc_SGL_Stop();                          
      }	
        
    }
}
 void user_adc_init(void)
{
	adc_gpio_config();
	adc_config();
}

user_adc.h


#ifndef _USER_ADC_H_
#define _USER_ADC_H_
 
#include "ddl.h"
 
#define ERROR_UNIT 0
#define TEMPERATURE_UNIT 1
#define APD_UNIT 2
 
#define ADC_WAITING 0
#define ADC_COMPLETED 1
 
extern float Vapd_sense;
extern uint8_t ADC_UNIT;
extern uint8_t ADC_FLAG;
 
void adc_gpio_config(void);
void adc_config(void);
void GPIO_Config_out(void); 
void user_adc_init(void);
 
void ADC_sampling(void);
void get_apd_adc(void);
 
#endif

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值