Arduino CH552 ADC的使用
📄CH552 ADC 简介
CH552 芯片提供 8 位的模拟数字转换器,包括电压比较器和 ADC 模块。该转换器具有 4 个模拟信号输入通道,可以分时采集,支持 0 到 VCC 模拟输入电压范围。
📗ADC 寄存器
-
- ADC 控制寄存器(ADC_CTRL)
- ADC 控制寄存器(ADC_CTRL)
- ADC 配置寄存器(ADC_CFG):
- ADC 数据寄存器(ADC_DATA):
📗ADC 功能
- 🌿ADC 采样模式配置步骤:
(1)、设置 ADC_CFG 寄存器中的 ADC_EN 位为 1,开启 ADC 模块,设置 bADC_CLK 选择频率。
(2)、设置 ADC_CTRL 寄存器中的 ADC_CHAN1/0,选择输入通道。
(3)、可选的,清零中断标志 ADC_IF。可选的,如果使用中断模式,还需要在此使能中断。
(4)、设置 ADC_CTRL 寄存器中的 ADC_START,启动一次 ADC 转换。
(5)、等待 ADC_START 变为 0,或者 ADC_IF 被置 1(如果之前已清零),表示 ADC 转换结束,可通过ADC_DATA 读取结果数据。该数据是输入电压相对于 VCC 电源电压的 255 等份的值,例如,结果数据是 47,说明输入电压接近 VCC 电压的 47/255。如果 VCC 电源电压也不确定,那么可以另测一个确定的参考电压值,再按比例计算出被测的输入电压值和 VCC 电源电压值。
(6)、如果再次设置 ADC_START 则可启动下一次 ADC 转换。 - 🌿电压比较器模式配置步骤:
(1)、设置 ADC_CFG 寄存器中的 CMP_EN 位为 1,开启电压比较器模块。
(2)、设置 ADC_CTRL 寄存器中的 ADC_CHAN1/0 和 CMP_CHAN,选择正相和反相输入端。
(3)、可选的,清零标志 CMP_IF。
(4)、任何时候都可查询 CMPO 位的状态获得当前比较器的结果。
(5)、如果 CMP_IF 变为 1,表示比较器的结果发生了变化。
上述被选择的模拟信号输入通道,其所在 GPIO 引脚必须设置为高阻输入模式、或者开漏输出模式并
且处于输出 1 的状态(相当于高阻输入),Pn_DIR_PU[x]=0,并且建议关闭上拉电阻和下拉电阻。
📙CH552 Arduino API函数支持
void analogWrite(__data uint8_t pin, __xdata uint16_t val)
uint8_t analogRead(__data uint8_t pin)
📝ADC 通道测试代码
✨开启ADC中断,需要在固件:
C:\Users\Administrator\AppData\Local\Arduino15\packages\CH55xDuino\hardware\mcs51\0.0.23\cores\ch55xduino\main.c
文件中,将PWM中断函数添加进去:void ADCInterrupt( void ) __interrupt(INT_NO_ADC);
/*
ADC精度值:0 - 255
AIN0(P1.1)
AIN1(P1.4)
AIN2(P1.5)
AIN3(P3.2)
*/
#include <Arduino.h>
#define LED_BUILTIN 17
volatile uint16_t count = 0; // 中断计数器
const uint16_t target = 500; // 500次中断(每次1ms)
static bool LED_FLAG = 0;
uint16_t UserData;
#define ADC_INTERRUPT 1
void ADCInit(uint8_t div)
{
ADC_CFG = ( ADC_CFG & ~bADC_CLK ) | div;
ADC_CFG |= bADC_EN; //ADC电源使能
#if ADC_INTERRUPT
ADC_IF = 0; //清空中断
IE_ADC = 1; //使能ADC中断
#endif
}
/*******************************************************************************
* Function Name : ADC_ChannelSelect(uint8_t ch)
* Description : ADC采样启用
* Input : uint8_t ch 采用通道
* Output : None
* Return : 成功 1
失败 0
*******************************************************************************/
uint8_t ADC_ChannelSelect(uint8_t ch)
{
if(ch == 0){ADC_CHAN1 =0;ADC_CHAN0=0;P1_DIR_PU &= ~bAIN0;} //AIN0
else if(ch == 1){ADC_CHAN1 =0;ADC_CHAN0=1;P1_DIR_PU &= ~bAIN1;} //AIN1
else if(ch == 2){ADC_CHAN1 =1;ADC_CHAN0=0;P1_DIR_PU &= ~bAIN2;} //AIN2
else if(ch == 3){ADC_CHAN1 =1;ADC_CHAN0=1;P3_DIR_PU &= ~bAIN3;} //AIN3
else return 0;
return 1;
}
/*******************************************************************************
* Function Name : VoltageCMPModeInit()
* Description : 电压比较器模式初始化
* Input : uint8_t fo 正向端口 0\1\2\3
uint8_t re 反向端口 1\3
* Output : None
* Return : 成功 1
失败 0
*******************************************************************************/
bool VoltageCMPModeInit(uint8_t fo,uint8_t re)
{
ADC_CFG |= bCMP_EN; //电平比较电源使能
if(re == 1){
if(fo == 0) {ADC_CHAN1 =0;ADC_CHAN0=0;CMP_CHAN =0;} //AIN0和AIN1
else if(fo == 2) {ADC_CHAN1 =1;ADC_CHAN0=0;CMP_CHAN =0;} //AIN2和AIN1
else if(fo == 3) {ADC_CHAN1 =1;ADC_CHAN0=1;CMP_CHAN =0; } //AIN3和AIN1
else return 0;
}
else if(re == 3){
if(fo == 0) {ADC_CHAN1 =0;ADC_CHAN0=0;CMP_CHAN =0;} //AIN0和AIN1
else if(fo == 1) {ADC_CHAN1 =0;ADC_CHAN0=1;CMP_CHAN =0;} //AIN1和AIN1
else if(fo == 2) {ADC_CHAN1 =1;ADC_CHAN0=0;CMP_CHAN =0;} //AIN2和AIN1
else return 0;
}
else return 0;
#if ADC_INTERRUPT
CMP_IF = 0; //清空中断
IE_ADC = 1; //使能ADC中断
#endif
return 1;
}
#if ADC_INTERRUPT
/*******************************************************************************
* Function Name : ADCInterrupt(void)
* Description : ADC 中断服务程序
*******************************************************************************/
void ADCInterrupt( void ) __interrupt(INT_NO_ADC) __using(1) //ADC中断服务程序,使用寄存器组1
{
if(ADC_IF == 1) //ADC完成中断
{
UserData = ADC_DATA; //取走ADC采样数据
ADC_IF = 0; //清空ADC中断标志
// printf(" %d ",UserData);
}
if(CMP_IF == 1) //电压比较完成中断
{
// UserData = ADC_CTRL&0x80 >> 7); //保存比较器结果
CMP_IF = 0; //清空比较器完成中断
}
}
#endif
// 定时器2中断服务函数
void Timer2Interrupt(void) __interrupt(INT_NO_TMR2) __using(3)
{
if (TF2) {
TF2 = 0; // 清除定时器2溢出标志
count++;
if (count >= target) {
count = 0;
LED_FLAG = !LED_FLAG;
// 执行500ms任务
digitalWrite(LED_BUILTIN, LED_FLAG); // 翻转LED
}
}
}
void Timer2_init()
{
// 配置定时器2
T2CON = 0x00; // 清除T2CON,设置为16位自动重载模式
T2MOD = 0x00; // 时钟=Fsys(24MHz)
// 计算定时器初值(24MHz时钟,1ms中断)
uint16_t timerValue = 65536 - 1000;//1ms
RCAP2L = timerValue & 0xFF; // 低8位
RCAP2H = (timerValue >> 8); // 高8位
// 装载初值
TL2 = RCAP2L;
TH2 = RCAP2H;
// 使能定时器2中断
ET2 = 1; // 使能定时器2中断
EA = 1; // 使能全局中断
// 启动定时器2
TR2 = 1;
}
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // 初始化为低电平(LED熄灭)
ADCInit( 0 );
Timer2_init();
}
// the loop function runs over and over again forever
void loop() {
#if ADC_INTERRUPT //ADC中断方式
EA = 1;
for(uint8_t i=0;i<4;i++){
USBSerial_print("AIN ");
USBSerial_flush();
USBSerial_print(i);
USBSerial_flush();
ADC_ChannelSelect( i ); //ADC采样电源开启和通道设置,i(0-3)表示采样通道
ADC_START = 1; //开始采样,采样完成进入中断
delay(100); //>=30us等待采集完成才能切换至下一通道
USBSerial_print(":");
USBSerial_flush();
USBSerial_println(UserData);
USBSerial_flush();
delay(1000);
}
#else
for(uint8_t i=0;i<4;i++){
USBSerial_print("AIN:");
USBSerial_flush();
USBSerial_print(i);
USBSerial_flush();
ADC_ChannelSelect( i ); //ADC采样初始化
ADC_START = 1; //开始采样,采样完成进入中断
while(ADC_START); //ADC_START变为0时,表示采样完成
USBSerial_println(ADC_DATA);
USBSerial_flush();//等待串口数据发送结束(串口数据发送必不可少)
delay(100); //模拟单片机干其他事
}
#endif
// USBSerial_println("Hello World123!");
// USBSerial_flush();
delay(1000); // wait for a second
// USBSerial_println("perseverance51");// wait for a second
// USBSerial_flush();//等待串口数据发送结束(串口数据发送必不可少)
}