STC15W系列有内部集成了8路10bit ADC转换器,该转换器是逐次比较型。其输入通道切换是通过内部模拟开关实现的。其原理结构如下:
转换结果存储在ADC-RES, ADC_RESL寄存器中。ADC-RES可以存储高8位也可存储高2位,缺省状态下是存储高8位,ADC_RESL存储低2位,当CLK_DIV寄存器的bit5设置为1时,ADC-RES存储高2位,ADC_RESL存储低8位。
与ADC相关的功能寄存器如下:
STC15W系列单片机的ADC没有单独的参考电压输入管脚,是以单片机的供电电压做参考电压。由于电源噪声,ADC转换结果会受到一些影响。如果要得到比较精确的结果,我们可以通过第9通道,读取内部BandGap参考电压的ADC结果,来比较修正。单片机的内部BandGap参考电压是很准确的。当下载程序时勾选下面红框中的项
BandGap参考电压将会存储在指定的内存位置中,当程序存储区内存大于1K时,存储位置为存储内存结尾位置减8,如STC15W4K61,BandGap参考电压的存储地址在0xF3FF-8,即0xF3F7。存储电压的单位为毫伏。
读取第九通道ADC值的方法如下:将寄存器P1ASF赋值为0x00,按正常读取通道0的ADC值的方法读取通道0的ADC值,其结果就是第九通道ADC值,即BandGap参考电压的ADC值。
基于目前对STC15W系列单片机ADC的了解,就可以动手写ADC的库函数了,新建两个文件分别以stcadc.h,stcadc.c存入C51 Template文件夹下Library文件夹中。完成后的头文件如下:
/*stcadc.h
Designed by Bill Liu
Version 0.0
Modified last by Bill Liu on 06/13/2022
*/
#ifndef __STCADC_H__
#define __STCADC_H__
#include "mtype.h"
#include "delay.h"
#include "stc15w4k.h"
extern ui16 code Vbg_ROM; // band gap voltage stord at memory end address -8, define at main.c or main.h
extern FSYSCLOCK FSCLK;
//
typedef enum
{
ADC_CH0 = 0x01, //ADC Channel P1.0
ADC_CH1 = 0x02, //ADC Channel P1.1
ADC_CH2 = 0x04, //ADC Channel P1.2
ADC_CH3 = 0x08, //ADC Channel P1.3
ADC_CH4 = 0x10, //ADC Channel P1.4
ADC_CH5 = 0x20, //ADC Channel P1.5
ADC_CH6 = 0x40, //ADC Channel P1.6
ADC_CH7 = 0x80 //ADC Channel P1.7
} ADC_CH;
//
typedef enum
{
CLK_C540 = 0, //540 clk cycle covert a time
CLK_C360, //360 clk cycle covert a time
CLK_C180, //180 clk cycle covert a time
CLK_C90, //90 clk cycle covert a time
}ADC_SPEED;
/***********************************************************
Function: ADC_EnableChn(BOOL mAble, ADC_CH myCh);
Return value: void
Discription: configure ADC_CH I/O ADC mode enable/disable
Example:
ADC_EnableChn(1, ADC_CH0); //ADC_CH1 ADC enable
***********************************************************************/
void ADC_EnableChn(BOOL mAble, ADC_CH myCh);
/***********************************************************
Function: ADC_PowerOn();
Return value: void
Discription: ADC power on
Example:
ADC_PowerOn();
***********************************************************************/
void ADC_PowerOn();
/***********************************************************
Function: ADC_PowerOff();
Return value: void
Discription: ADC power switch off
Example:
ADC_PowerOff();
***********************************************************************/
void ADC_PowerOff();
/***********************************************************
Function: ADC_SpeedConfig(ADC_SPEED mSpeed);
Return value: void
Discription: configure ADC speed
Example:
ADC_SpeedConfig(CLK_C360);
***********************************************************************/
void ADC_SpeedConfig(ADC_SPEED mSpeed);
/***********************************************************
Function: ADC_GetFlag();
Return value: void
Discription: get ADC finish flag
Example:
ui8 tem = ADC_GetFlag();
***********************************************************************/
ui8 ADC_GetFlag();
/***********************************************************
Function: ADC_GetFlag();
Return value: void
Discription: get ADC finish flag
Example:
ui8 tem = ADC_GetFlag();
***********************************************************************/
void ADC_ResetFlag();
/***********************************************************
Function: ADC_Start();
Return value: void
Discription: ADC start
Example:
ADC_Start();
***********************************************************************/
void ADC_Start();
/***********************************************************
Function: ADC_ChnSel(ADC_CH mSel);
Return value: void
Discription: select ADC channel
Example:
ADC_ChnSel(ADC_CH0);
***********************************************************************/
void ADC_ChnSel(ADC_CH mSel);
/***********************************************************
Function: ADC_Init(ADC_SPEED mSel);
Return value: void
Discription: init ADC speed and set power on
Example:
ADC_ChnSel(CLK_C360);
***********************************************************************/
void ADC_Init(ADC_SPEED mSel);
/***********************************************************
Function: ADC_GetResult(ADC_CH Chx);
Return value: ui16
Discription: get x channel ADC result
Example:
ui16 tem = ADC_GetResult(ADC_CH2);
***********************************************************************/
ui16 ADC_GetResult(ADC_CH Chx);
/***********************************************************
Function: ADC_GetBandGapRes();
Return value: ui16
Discription: get band gap voltage ADC result
Example:
ui16 tem = ADC_GetBandGapRes();
***********************************************************************/
ui16 ADC_GetBandGapRes();
/***********************************************************
Function: ADC_GetBgVoltage();
Return value: ui16
Discription: get mcu band voltage
Example:
ui16 tem =ADC_GetBgVoltage(); //unit: mV
***********************************************************************/
ui16 ADC_GetBgVoltage();
/***********************************************************
Function: ADC_ChnVoltage(ADC_CH Chx);
Return value: ui16
Discription: get x channel input voltage
Example:
ui16 tem =ADC_ChnVoltage(ADC_CH2); //unit: mV
***********************************************************************/
ui16 ADC_ChnVoltage(ADC_CH Chx);
#endif
完成后的源代码如下:
/*stcadc.c
Designed by Bill Liu
Version 0.0
Modified last by Bill Liu on 12/21/2021
*/
#include "stcadc.h"
//***********************************************************************
void ADC_EnableChn(BOOL mAble, ADC_CH myCh)
{
if(mAble)
P1ASF |= myCh;
else
P1ASF &= ~myCh;
}
//End of ADC_EnableChn(BOOL mAble, ADC_CH myCh)
//*****************************************************
void ADC_PowerOn() //switch on A/D power
{
ADC_CONTR |=0x80;
}
//End of ADC_PowerOn()
//*****************************************************
void ADC_PowerOff() //switch off A/D power
{
ADC_CONTR &=0x7F;
}
//End of ADC_PowerOff()
//*****************************************************
void ADC_SpeedConfig(ADC_SPEED mSpeed)
{
ui8 tem = ADC_CONTR;
SetBits(&tem, 6, 5, mSpeed);
ADC_CONTR = tem;
}
//End of ADC_SpeedConfig(ADC_SPEED mSpeed)
//*****************************************************
ui8 ADC_GetFlag()//get A/D flag state
{
return GetBit(ADC_CONTR, 4);
}
//End of ADC_GetFlag()
//*****************************************************
void ADC_ResetFlag() //reset A/D flag
{
ADC_CONTR &= ~0x10;
}
//End of ADC_ResetFlag()
//*****************************************************
void ADC_Start() //start A/D conversion
{
ui8 tem = ADC_CONTR;
tem |= 0x80;
tem |= 0x08;
ADC_CONTR = tem;
}
//End of ADC_Start()
//*****************************************************
void ADC_ChnSel(ADC_CH mSel)
{
ui8 tem = ADC_CONTR;
switch(mSel)
{
case ADC_CH0:
SetBits(&tem,2,0,0x00);
break;
case ADC_CH1:
SetBits(&tem,2,0,0x01);
break;
case ADC_CH2:
SetBits(&tem,2,0,0x02);
break;
case ADC_CH3:
SetBits(&tem,2,0,0x03);
break;
case ADC_CH4:
SetBits(&tem,2,0,0x04);
break;
case ADC_CH5:
SetBits(&tem,2,0,0x05);
break;
case ADC_CH6:
SetBits(&tem,2,0,0x06);
break;
case ADC_CH7:
SetBits(&tem,2,0,0x07);
break;
}
ADC_CONTR = tem;
}
//End of ADC_ChnSel(ui8 mSel)
//****************************************************
void ADC_Init(ADC_SPEED mSel)
{
CLK_DIV &= ~0x20; //ADC_RES store high 2 bits
ADC_RES=0;
ADC_RESL=0;
ADC_SpeedConfig(mSel);
ADC_ResetFlag();
ADC_PowerOn();
Delayxms(2,FSCLK);
}
//End of ADC_Init(ADC_SPEED mSel)
//****************************************************
ui16 ADC_GetResult(ADC_CH Chx)
{
ui16 tem;
ADC_RES=0;
ADC_RESL=0;
ADC_EnableChn(1, Chx);
ADC_ChnSel(Chx);
ADC_ResetFlag();
ADC_Start();//start A/D conversion
Delayxus(2,FSCLK);
while(!ADC_GetFlag());
ADC_ResetFlag();
tem = ADC_RES;
tem <<= 2;
tem += ADC_RESL;
return tem;
}
//End of ADC_GetResult(ADC_CH Chx)
//****************************************************
ui16 ADC_GetBandGapRes()
{
ui16 tem;
P1ASF = 0;
ADC_ChnSel(ADC_CH0);
ADC_ResetFlag();
ADC_Start();//start A/D
Delayxus(2,FSCLK);
while(!ADC_GetFlag());
ADC_ResetFlag();
tem = ADC_RES;
tem <<= 2;
tem += ADC_RESL;
return tem;
}
//End of ADC_GetBandGapRes()
//****************************************************
ui16 ADC_ChnVoltage(ADC_CH Chx)
{
return ((ui32)ADC_GetResult(Chx) * Vbg_ROM / ADC_GetBandGapRes());
}
//End of ADC_ChnVoltage(ADC_CH Chx)
测试程序头文件:
/*main.h
Designed by Bill Liu
Version 0.0
Modified last by Bill Liu on 12/15/2021
*/
#ifndef __MAIN_H_
#define __MAIN_H_
#include "mtype.h"
#include "delay.h"
#include "stcuart.h"
#include "stcio.h"
#include "myport.h"
#include "stcadc.h"
const FSYSCLOCK FSCLK = F30MHz; //define system clock frequency 30MHz, FSYSCLOCK was defined in mtype.h
const ui32 FSCLK1 = 30000000L; //define system clock frequency 30MHz
ui16 code Vbg_ROM _at_ 0xf3f7; //band gap voltage stord at memory end address -8
i8 mstr[25] ="";
void SundBuzzerx10ms(ui8 x);
测试程序源文件:
/*main.c
Designed by Bill Liu
Version 0.0
Modified last by Bill Liu on 12/16/2021
*/
#include "main.h"
void main()
{
STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
P3 = 0xFF;
P0 = 0xFF;
UartS1_Init(VBAUD_8BITS,G1,T2,9600);
STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
ADC_Init(1);
SundBuzzerx10ms(50);
Delay10xms(50, FSCLK);
SundBuzzerx10ms(50);
while(1)
{
LongToString(Vbg_ROM,mstr);
UartS1_SendString(mstr);
UartS1_SendString("\r\n");
LongToString(ADC_GetBandGapRes(),mstr);
UartS1_SendString(mstr);
UartS1_SendString("\r\n");
LongToString(ADC_GetResult(4),mstr);
UartS1_SendString(mstr);
UartS1_SendString("\r\n");
LongToString(ADC_ChnVoltage(ADC_CH4),mstr);
UartS1_SendString(mstr);
UartS1_SendString("\r\n");
Delay10xms(200, FSCLK);
}
}
//End of main()
//*********************************************
void SundBuzzerx10ms(ui8 x)
{
BUZZER = 0;
Delay10xms(x, FSCLK);
BUZZER = 1;
}
//End of SundBuzzerx10ms(ui8 x)
编译后下载到IAP15W4K61S4单片机中,在串口口助手中得到的结果如下:
说明库函数能够正常运行。以上代码文件已上传到CSDN,文件名为STC ADC Library Source Code.rar, 如下离线查看,可去下载。