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