一:ADC介绍;
ADC是一种模数转换器,主要作用是将真实世界产生的如温度、压力、声音、指纹或者图像等模拟信号转换成更容易处理的数字形式。ADC/DAC的简要工作原理和过程如下图所示:
CS88F003 内嵌一个分辨率为 12 位的逐次逼近型模拟数字转换器(ADC),A/D 模块负责将管脚上的模拟电压信号转换为 12 位二进制数据。其支持8通道单端外部输入模式和内部1.2V带隙基准VBG输入模式,并且还支持比较输出功能,其模块时钟源由系统时钟提供,参考电压 VREF 可选择电源电压VDD 或者 PT30 外部输入电压。
CS88F003 ADC模块框图如下图所示:
二:CS88F003 ADC模块使用;
使用注意点:
①:不管是否使用中断,想要开启一次转换,就必须重新设置ADCON寄存器的bit4位一次;
②:CS88F003ADC只能选择VDD或者外部(PT30)作为参考电压;
③:此芯片的ADC转换速率不要设置为最快,设置为中等即可!
2.1:通过轮询方式等待ADC转换完成;
ADC模块配置步骤:
/**
* @name Adc_init
* @功能 :初始化ADC模块
* @param 无
* @retval: 无
*/
void Adc_init(void)
{
/*1:第一步关闭校准,选择基准电压*/
ADOC = 0;
/*2:第二步设置ADC周期和采样时间*/
ADT = 0x83;
/*3:第三步选择读取ADC的转换值*/
ADDL &= ~(0x10);
}
获取ADC通道采样值步骤:
/**
* @name Adc_get_value
* @功能 :获取ADC通道的采样值
* @param adc_ch 需要读取的通道号
@arg 0--AN0
@arg 1--AN1
@arg 2--AN2
@arg 3--AN3
@arg 4--AN4
@arg 5--AN5
@arg 6--AN6
@arg 7--AN7
* @retval: adc_value 相应通道的ADC采集值
*通道的值如果返回0xffff,说明转换异常!
*/
Uint_16U Adc_get_value(Uint_8U adc_ch)
{
Uint_16U timeout = 0;
Uint_16U adc_value = 0xffff;
Uint_8U addl_value = 0;
/*确保只读取AN0~AN7通道*/
adc_ch &= 0x07;
/*1:第一步打开ADC模块关闭比较功能*/
ADCON = 0x80;
/*2:第二步选择ADC通道*/
ADCON |= adc_ch;
/*3:第三步启动ADC转换*/
ADCON |= 0x10;
/*4:第四步等待ADC转换完成*/
while((ADCON & 0x10))
{
timeout++;
if(timeout > 1000)
{
/*超时还未转换完成,外部程序应该对此判断出错*/
timeout = 0;
/*返回0xffff代表转换异常*/
return adc_value;
}
}
//6:第六步获取转换值
/*获取转换结果高位值*/
adc_value = ADDH;
adc_value <<= 4;
/*获取转换结果低位值*/
addl_value = ADDL;
addl_value &= 0x0f;
/*合并高低位值,返回ADC转换值*/
adc_value += addl_value;
return adc_value;
}
轮询等待方式读取ADC值实验主函数程序:
/******************************************************************************************
程序说明:本程序主要是验证ADC的采集功能。主要逻辑是配置ADC模块后,在轮询系统中开启转换,然后读出转
换后的ADC值,最后再通过串口将AD值发出,可用逻辑分析仪查看。
注:
①:注意如果是测试的话最好通过直流电源给到ADC的输入口,因为当你想用ADC去采集PWM的平均电压时,
很有可能出现采集值差别很大。除非ADC的采集频率远大于PWM频率,采集多次后取平均值!
②:CS88F003存储模式是大端模式,即高位存储在地址低位。
③: ADC_CH0----PT17
TXD--------PT16
④:输入电压不能大于VDD
*******************************************************************************************/
void uart1_send_init(void);
void uart1_send_data(Uint_8U *buff, Uint_16U str_len);
void main(void)
{
Uint_16U adc_ch0_value;
Sys_Clk_Set_IRCH(SYS_CLK_8M); //芯片默认校准为16M,分频后系统主频8M
SysClk_Delay(50);
WDT_OFF();
/*初始化ADC模块*/
Adc_init();
/*初始化uart1模块*/
uart1_send_init();
while(1)
{
/*获取ADC-CH0的采样值*/
adc_ch0_value = Adc_get_value(0);
/*将采样数据发出,注意003是大端存储模式*/
uart1_send_data((Uint_8U *)&adc_ch0_value,2);
SysClk_Delay(50);
}
}
/*************************************************uart1函数************************************************/
/**
* @name uart1_send_init
* @功能 :初始化uart1模块
* @param 无
* @retval: 无
*/
void uart1_send_init(void)
{
/*使能端口复用寄存器,PT16复用为UART1_TXD*/
PT1_MUX0 |= 0x40;
/*设置uart模块的频率,uart1选择1分频也就是模块频率等于系统主频*/
UART1_CLKS = 0x00;
/*设置uart传输数据的波特率,uart1传输速率设置为115200*/
UART1_CLKDIVH = 0x00;
UART1_CLKDIVL = 0x45;
/*设置uart模块的帧格式,uart1设置为10位帧格式(1起始+8数据+1停止)*/
UART1_CTRL &= ~(0x06);
/*使能uart1模块*/
UART1_CTRL |= 0x01;
}
/**
* @name uart1_send_data
* @功能 :串口输出接口
* @param
@arg buff 需要输出数组
@arg str_len 数组大小
* @retval: 无
*/
void uart1_send_data(Uint_8U *buff, Uint_16U str_len)
{
Uint_16U i;
for(i=0;i<str_len;i++)
{
UART1_TXD = buff[i];
while(UART1_STATE & 0x40);
}
}
2.2:通过中断方式检测ADC是否转换完成;
ADC模块配置步骤:
/**
* @name Adc_Int_init
* @功能 :初始化ADC模块
* @param adc_ch 需要读取的通道号
@arg 0--AN0
@arg 1--AN1
@arg 2--AN2
@arg 3--AN3
@arg 4--AN4
@arg 5--AN5
@arg 6--AN6
@arg 7--AN7
* @retval: 无
*/
void Adc_Int_init(Uint_8U adc_ch)
{
/*1:第一步关闭校准,选择基准电压*/
ADOC = 0;
/*2:第二步设置ADC周期和采样时间*/
ADT = 0x83;
/*3:第三步选择读取ADC的转换值*/
ADDL &= ~(0x10);
/*4:第四步打开ADC模块关闭比较功能*/
ADCON = 0x80;
/*5:第五步确保只读取AN0~AN7通道*/
adc_ch &= 0x07;
/*6:第六步选择ADC通道*/
ADCON |= adc_ch;
/*7:第七步选择ADC中断输出口,INT0*/
IRQ_SEL0 |= 0x04;
/*8:第八步使能ADC中断*/
IRQ_EN0 |= 0x04;
/*9:第九步使能全局中断和INT0*/
IE = 0x81;
/*10:第十步启动ADC开始转换*/
ADCON |= 0x10;
}
中断处理程序逻辑步骤:
volatile Uint_16U adc_value = 0; //ADC值
volatile Uint_8U addl_value = 0; //寄存器低位值
volatile Uint_8U adc_flag = 0; //转换标志位
void IRQ_0() interrupt 0
{
/*1:第一步判断是否产生了ADC中断*/
if(IRQ_STAT0 & 0x04)
{
/*2:第二步清除中断标志位*/
IRQ_CLR0 |= 0x04;
/*3:第三步判断是否转换完成*/
if(!(ADCON & 0x10))
{
//4:第四步获取转换值
/*获取高位寄存器值*/
adc_value = ADDH;
adc_value <<= 4;
/*获取低位寄存器值*/
addl_value = ADDL;
addl_value &= 0x0f;
/*adc值*/
adc_value += addl_value;
/*5:第五步根据需要重新开启ADC转换*/
ADCON |= 0x10;
/*置位标志,可在外部使用ADC值*/
adc_flag = 1;
}
}
}
中断检测方式读取ADC值实验主函数程序:
/******************************************************************************************
程序说明:本程序主要是验证ADC的采集功能(在中断中获取ADC值)。主要逻辑是配置ADC模块后,当ADC转换完
成触发中断,然后读出转换后的ADC值,最后再通过串口将AD值发出,可用逻辑分析仪查看。
注:
①:注意如果是测试的话最好通过直流电源给到ADC的输入口,因为当你想用ADC去采集PWM的平均电压时,
很有可能出现采集值差别很大。除非ADC的采集频率远大于PWM频率,采集多次后取平均值!
②:CS88F003存储模式是大端模式,即高位存储在地址低位。
③: ADC_CH0----PT17
TXD--------PT16
④:输入电压不能大于VDD
*******************************************************************************************/
extern volatile Uint_16U adc_value ; //ADC值
extern volatile Uint_8U adc_flag ; //转换标志位
void uart1_send_init(void);
void uart1_send_data(Uint_8U *buff, Uint_16U str_len);
void main(void)
{
Sys_Clk_Set_IRCH(SYS_CLK_8M); //芯片默认校准为16M,分频后系统主频8M
SysClk_Delay(50);
WDT_OFF();
/*初始化ADC模块*/
Adc_Int_init(0);
/*初始化uart1模块*/
uart1_send_init();
while(1)
{
if(adc_flag)
{
uart1_send_data((Uint_8U*)&adc_value, 2);
adc_flag = 0;
}
SysClk_Delay(50);
}
}
/*************************************************uart1函数************************************************/
/**
* @name uart1_send_init
* @功能 :初始化uart1模块
* @param 无
* @retval: 无
*/
void uart1_send_init(void)
{
/*使能端口复用寄存器,PT16复用为UART1_TXD*/
PT1_MUX0 |= 0x40;
/*设置uart模块的频率,uart1选择1分频也就是模块频率等于系统主频*/
UART1_CLKS = 0x00;
/*设置uart传输数据的波特率,uart1传输速率设置为115200*/
UART1_CLKDIVH = 0x00;
UART1_CLKDIVL = 0x45;
/*设置uart模块的帧格式,uart1设置为10位帧格式(1起始+8数据+1停止)*/
UART1_CTRL &= ~(0x06);
/*使能uart1模块*/
UART1_CTRL |= 0x01;
}
/**
* @name uart1_send_data
* @功能 :串口输出接口
* @param
@arg buff 需要输出数组
@arg str_len 数组大小
* @retval: 无
*/
void uart1_send_data(Uint_8U *buff, Uint_16U str_len)
{
Uint_16U i;
for(i=0;i<str_len;i++)
{
UART1_TXD = buff[i];
while(UART1_STATE & 0x40);
}
}
2.3:ADC的比较功能;
功能介绍: 003的ADC比较功能实际上就是一个阈值电压,初始时设置好阈值,当AD口采集到的电压大于或者小于这个阈值时就会触发中断。但是我根据实测发现,这个中断在仿真全速运行时只能触发一次,除非重新进仿真,也就是说这个功能是一次性的。
ADC模块配置步骤:
/**
* @name Adc_Cmp_init
* @功能 :配置ADC比较模式
* @param adc_ch 需要比较的通道
@arg 0--AN0
@arg 1--AN1
@arg 2--AN2
@arg 3--AN3
@arg 4--AN4
@arg 5--AN5
@arg 6--AN6
@arg 7--AN7
* @retval: 无
*/
void Adc_Cmp_init(Uint_8U adc_ch)
{
/*1:第一步关闭校准,选择基准电压*/
ADOC = 0;
/*2:第二步设置ADC周期和采样时间*/
ADT = 0x83;
/*3:第三步选择读取ADC的转换值*/
ADDL &= ~(0x10);
/*4:第四步打开ADC模块,开启比较功能,选择比较模式,例如选择大于模式*/
ADCON = (0x80 | 0x40 |0x20);
/*5:第五步选择ADC通道*/
ADCON |= adc_ch;
/*6:第六步选择ADC中断输出口,INT0*/
IRQ_SEL0 |= 0x04;
/*7:第七步使能ADC中断*/
IRQ_EN0 |= 0x04;
/*8:第八步使能全局中断和INT0*/
IE = 0x81;
/*9:第九步设置比较电压值,例如当检测到电压大于3v,触发一次中断*/
ADDH = 0x99;
ADDL = 0x09;
/*10:第十步启动ADC开始转换*/
ADCON |= 0x10;
}
中断处理程序逻辑步骤:
volatile Uint_16U adc_value = 0; //ADC比较值
volatile Uint_8U addl_value = 0; //低位寄存器值
volatile Uint_8U adc_flag = 0; //比较器中断标志
void IRQ_0() interrupt 0
{
/*1:第一步判断是否产生了ADC中断*/
if(IRQ_STAT0 & 0x04)
{
/*2:第二步清除中断标志位*/
IRQ_CLR0 |= 0x04;
/*翻转pt00引脚电平*/
PT0_DOUT ^= 0x01;
/*置位标志,说明ADC采样值触发比较器中断了*/
adc_flag = 1;
/*3:可以选择性读取此时adc转换值*/
if(!(ADCON & 0x10))
{
adc_value = ADDH;
adc_value <<= 4;
addl_value = ADDL;
addl_value &= 0x0f;
adc_value += addl_value;
}
}
}
ADC比较功能实验主函数程序:
/*****************************************************************************************************************
程序说明:本程序主要是验证ADC的比较功能,其基本逻辑是在主程序中配置好ADC的比较功能后就在轮询系统中检测中断标志,
一旦电压大于3v,触发中断后标志位就会置1,并且还会在中断中将PT00引脚翻转,最后在轮询系统中通过串口将采样值发送出去;
注:
①:注意如果是测试的话最好通过直流电源给到ADC的输入口,因为当你想用ADC去采集PWM的平均电压时,
很有可能出现采集值差别很大。除非ADC的采集频率远大于PWM频率,采集多次后取平均值!
②:CS88F003存储模式是大端模式,即高位存储在地址低位。
③: ADC_CH0----PT17
TXD--------PT16
④:输入电压不能大于VDD
⑤:例如在VDD=5V时选择触发模式为大于模式,设置的比较值是0x999(换算电压为3v),当ADC采集的引脚电压大于3v时,
则会触发ADC中断。
⑥:经测试在整个程序运行周期内只会触发一次中断,例如设置了3V的比较值,在程序运行时检测到一次3.5v触发了中断,
即使后面再次检测到3.5v也不会响应中断了!
****************************************************************************************************************/
extern volatile Uint_16U adc_value; //ADC转换值
extern volatile Uint_8U adc_flag ; //转换标志位
void uart1_send_init(void);
void uart1_send_data(Uint_8U *buff, Uint_16U str_len);
void main(void)
{
Sys_Clk_Set_IRCH(SYS_CLK_8M); //芯片默认校准为16M,分频后系统主频8M
SysClk_Delay(50);
WDT_OFF();
/*初始化IO口,PT00初始置高电平*/
PT0_OE |= 0x01;
PT0_DOUT |= 0x01;
/*初始化ADC模块*/
Adc_Cmp_init(0);
/*初始化uart1模块*/
uart1_send_init();
while(1)
{
/*如果触发比较中断,则通过串口将触发中断时的AD值发送出去*/
if(adc_flag)
{
uart1_send_data((Uint_8U *)&adc_value, 2);
adc_flag = 0;
}
SysClk_Delay(50);
}
}
/*************************************************uart1函数************************************************/
/**
* @name uart1_send_init
* @功能 :初始化uart1模块
* @param 无
* @retval: 无
*/
void uart1_send_init(void)
{
/*使能端口复用寄存器,PT16复用为UART1_TXD*/
PT1_MUX0 |= 0x40;
/*设置uart模块的频率,uart1选择1分频也就是模块频率等于系统主频*/
UART1_CLKS = 0x00;
/*设置uart传输数据的波特率,uart1传输速率设置为115200*/
UART1_CLKDIVH = 0x00;
UART1_CLKDIVL = 0x45;
/*设置uart模块的帧格式,uart1设置为10位帧格式(1起始+8数据+1停止)*/
UART1_CTRL &= ~(0x06);
/*使能uart1模块*/
UART1_CTRL |= 0x01;
}
/**
* @name uart1_send_data
* @功能 :串口输出接口
* @param
@arg buff 需要输出数组
@arg str_len 数组大小
* @retval: 无
*/
void uart1_send_data(Uint_8U *buff, Uint_16U str_len)
{
Uint_16U i;
for(i=0;i<str_len;i++)
{
UART1_TXD = buff[i];
while(UART1_STATE & 0x40);
}
}