STC8H单片机使用ADC第15通道测量外部电压及电池电压详解
STC8H 系列 ADC 的第 15 通道用于测量内部参考信号源,由于内部参考信号源很稳定,约为 1.19V, 且不会随芯片的工作电压的改变而变化,所以可以通过测量内部 1.19V 参考信号源,然后通过 ADC 的 值便可反推出外部电压或外部电池电压。以下是如何设置和读取 ADC 第 15 通道的详细步骤:
1. 硬件连接
确保你要测量的电压信号(如外部电压或电池电压)通过适当的电路连接到 ADC 输入引脚。对于 STC8H 单片机,ADC 第 15 通道通常连接到外部电压输入引脚。
2. 配置 ADC
2.1 启用 ADC 功能
首先,你需要在单片机的初始化代码中启用 ADC 功能。通常在 STC8H 系列单片机中,这涉及到设置相关的寄存器
// 假设使用 STC8H 的标准库函数或自定义函数
void ADC_Init(void) {
// 使能 ADC 模块
ADCCFG |= 0x01; // 开启 ADC 功能,具体寄存器设置请查阅数据手册
}
2.2 配置 ADC 输入通道
选择 ADC 第 15 通道作为输入源。通常可以通过设置 ADC 通道选择寄存器来完成这一操作。
void ADC_SetChannel(uint8_t channel) {
// 选择 ADC 通道
ADCCON |= (channel & 0x0F); // 设置通道编号,具体寄存器设置请查阅数据手册
}
2.3 配置 ADC 转换参数
设置 ADC 的采样精度、参考电压等参数,以确保测量精度和范围。
void ADC_Config(void) {
ADCCON |= 0x40; // 设置为 12 位分辨率,具体设置请查阅数据手册
}
3. 启动 ADC 转换
启动 ADC 转换并等待转换完成。ADC 转换可以通过软件触发,也可以通过硬件触发。以下是软件触发的例子:
uint16_t ADC_Read(void) {
// 启动 ADC 转换
ADCCON |= 0x80; // 开始转换,具体寄存器设置请查阅数据手册
// 等待转换完成
while (!(ADCCON & 0x08)); // 等待完成标志位,具体设置请查阅数据手册
// 读取结果
uint16_t result = (ADCH << 8) | ADCL; // 读取结果寄存器,具体寄存器设置请查阅数据手册
return result;
}
4. 计算实际电压
ADC 输出值需要转换成实际电压值。计算电压值的公式通常是:
5. 完整示例代码
uart.h
#ifndef _UART_H_
#define _UART_H_
#include "system.h"
//===== 串口使能 ===========
#define U1_ENABLE
#define UART_SBUF_LEN 32
#define UART_RBUF_LEN 32
#define _Debug_ 0 //调试模式设置 1开启调试模式 0关闭调试模式
#if _Debug_
#define Debug printf
#else
#define Debug /##/
#endif
#define PRINTF_SEL 1
typedef struct
{
u8 rBuf[UART_RBUF_LEN];
u16 rCnt;
bool_t rOver;
}
Usart_Read_STR;
extern idata Usart_Read_STR U1_rSTR;
extern u8 U1_sBusy_Flag;
//extern u16 U1_swithcTime;
void Uart1_Init(u32 baud);
void Uart1_Send_Byte(u8 byte);
void Uart1_Send_Str(u8 *string);
void Uart1_Send_xStr(u8 *string,u16 x);
void down_load(u8 *RxBufer);
void CleanBufer(uint8 *ptr,uint16 Size);
/**********************************
* 系统时钟中断服务程序
* 根据定时器编号更改中断入口
* 定时器0 -- 1
* 定时器1 -- 3
**********************************/
extern void Check_U1_rOver(void);
#endif
uart.c
#include "uart.h"
u8 Uart_sBuf[UART_SBUF_LEN]; //串口共用发送缓存
/**********************************************************
* 串口1相关函数
*/
#ifdef U1_ENABLE
u8 U1_sBusy_Flag = 0; //串口1发送忙碌
idata Usart_Read_STR U1_rSTR; //串口1接收缓存
//u16 U1_swithcTime=0;
/*----------------------------
* 初始化串口1
* ch:串口引脚映射通道
* 0:P3.0/RxD, P3.1/TxD
* 1:P3.6/RxD_2, P3.7/TxD_2
* 2:P1.6/RxD_3, P1.7/TxD_3
* 注意STC15W408AS无定时器1
----------------------------*/
#define U1_BAUD 9600
#define S1_S0 0x40 //P_SW1.6
#define S1_S1 0x80 //P_SW1.7
void Uart1_Init(u32 baud)
{
ACC = P_SW1;
ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0
P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD)
SCON = 0x50; //8位可变波特率
T2L = (65536 - (MAIN_Fosc/4/baud)); //设置波特率重装值
T2H = (65536 - (MAIN_Fosc/4/baud))>>8;
AUXR |= 0x15; //T2为1T模式,启动定时器2,选择定时器2为串口1的波特率发生器
ES = 1; //使能串口1中断
EA = 1;
}
/*----------------------------
* 串口发送1个字节
----------------------------*/
void Uart1_Send_Byte(u8 byte)
{
U1_sBusy_Flag = 1;
SBUF = byte;
while(U1_sBusy_Flag);
}
/*----------------------------
* 串口发送字符串
----------------------------*/
void Uart1_Send_Str(u8 *string)
{
while(*string != 0)
{
U1_sBusy_Flag = 1;
SBUF = *string++;
while(U1_sBusy_Flag);
}
}
/*----------------------------
* 串口发送指定长度字符串
----------------------------*/
void Uart1_Send_xStr(u8 *string,u16 x)
{
while(x--)
{
U1_sBusy_Flag = 1;
SBUF = *string++;
while(U1_sBusy_Flag);
}
}
/************************************* Printf 函数 **********************************************************
通过查阅资料,51 单片机 通过 printf 打印格式化数据的时候,不能使用 %d 之类的占位符,转而使用以下占位符:
待打印数据的大小 格式化的符号 备注
占位符 8位 bd/bu bd:有符号8位数据 bu:无符号8位数据
占位符 16位 hd/hu hd:有符号16位数据 hu:无符号16位数据
占位符 32位 ld/lu ld:有符号32位数据 lu:无符号32位数据
char a = 1;
int b = 12365;
long c = 0x7FFFFFFF;
unsigned char x = 'A';
unsigned int y = 54321;
unsigned long z = 0x4A6F6E00;
float f = 10.0;
float g = 22.95;
printf ("char %bd int %d long %ld\n",a,b,c);
printf ("Uchar %bu Uint %u Ulong %lu\n",x,y,z);
printf ("xchar %bx xint %x xlong %lx\n",x,y,z); //16进制显示
printf ("String %s is at address %p\n",buf,p);
printf ("%f != %g\n", f, g);
printf ("%*f != %*g\n", (int)8, f, (int)8, g);
***********************************************************************************************************************/
#if(PRINTF_SEL == 1)
char putchar(char c)
{
Uart1_Send_Byte(c);
return c;
}
#elif(PRINTF_SEL == 2)
char putchar(char c)
{
UART2_Send_byte(c);
return c;
}
#elif(PRINTF_SEL == 3)
char putchar(char c)
{
UART_Send_byte(UART3,c);
return c;
}
#elif(PRINTF_SEL == 4)
char putchar(char c)
{
UART_Send_byte(UART4,c);
return c;
}
#endif
#endif
main.c
#include "STC8.h"
#include "Headers.h"
#define MAIN_Fosc 24000000L //定义主时钟
/========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2022-6-3
// 备注:
//========================================================================
void delay_ms(int ms)
{
u16 i;
do{
i = MAIN_Fosc / 10000;
while(--i); //10T per loop
}while(--ms);
}
void ADCInit()
{
ADCTIM = 0x3f; //设置 ADC 内部时序,ADC采样时间建议设最大值
ADCCFG = 0x2f; //设置 ADC 时钟为系统时钟/2/16
ADC_CONTR = 0x80; //使能 ADC 模块
}
u16 ADCRead(int channel)
{
ADC_RES = 0;
ADC_RESL = 0;
ADC_CONTR = (ADC_CONTR & 0xF0) | 0x40 | channel; //启动 AD 转换
_nop_();
_nop_();
_nop_();
_nop_();
while((ADC_CONTR & 0x20) == 0) ; //wait for ADC finish
ADC_CONTR &= ~0x20; //清除ADC结束标志
return (((u16)ADC_RES << 8) | ADC_RESL);
}
void main(void)
{
float vcc=0.00;
u16 res;
u8 i;
Uart1_Init(9600); //9600bps@24.000MHz
ADCInit();
while(1)
{
ADCRead(15);
ADCRead(15); //前两个数据建议丢弃
res = 0;
for (i=0; i<8; i++)
{
res += ADCRead(15); //读取 8 次数据
}
res >>= 3; //取平均值
vcc = (4096L * 1.19 / res); //(12 位 ADC 算法)计算 VREF 管脚电压,即电池电压
printf("ADC_convert:%g mV \r\n", vcc);
delay_ms(500);
}
}
6、测试结果
由于没有读取内部基准电压结果会有一点小误差。实际值2.76V,ADC测量的外部电压为2.79V。需要更高精度的可以把1.19换成 ,读取内部 1.19V 参考信号源-BGV 值。具体可以研究一下手册
7. 注意事项
以上是如何使用 STC8H 单片机的 ADC 第 15 通道测量外部电压或电池电压的详细步骤。如果有进一步的问题或需要更多的帮助,请随时询问!
作者:桂涛123