• 学习笔记|模数转换器|ADC原理|STC32G单片机视频开发教程(冲哥)|第十七集:ADC采集


    1.模数转换器(ADC)是什么?

    参考资料adc模数转换器的作用
    模数转换器即A/D转换器,或简称ADC(Analog-to-digital converter),通常是指一个将模拟信号转变为数字信号的电子元件。
    模拟信号 -> 电压
    数字信号 -> 0和1组成的二进制数
    那我们思考下我们单片机是怎么把模拟信号转化为数字信号的呢?
    原理演示视频:见B站《逐次逼近型ADC转换过程的动画演示》
    简单的总结一下ADC到底是一个什么样的原理?
    如果没有超,就写1,如果超了就写0.
    分别测量一下两组板子的高度,从大到小一级一级的给它比较上去
    结束以后是221.
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    如果增加比较的位数,精度更高:
    在这里插入图片描述
    在这里插入图片描述

    当然这个位数也不是能一直无限制的高下去,毕竟环境噪声也会对他有所千扰(精度太高,吹口气就有误差了)。
    一般的话12位到16位绰绰有余。这就是ADC的一个转化的一个过程,一位一位的逐次转化。

    手册说明:

    ADC模数转换、传统DAC实现
    STC32G系列单片机内部集成了一个12位高速AD转换器。ADC的时钟频率为系统频率。分频再经过用户设置的分频系数进行再次分频(ADC的时钟频率范围为SYSclk/2/1~SYSclk/2/16)。
    ADC转换结果的数据格式有两种:左对齐和右对齐。可方便用户程序进行读取和引用。
    注意:ADC 的第15通道是专门测量内部 1.19V参考信号源的通道,参考信号源值出厂时校准为1.19V,由于制造误差以及测量误差,导致实际的内部参考信号源相比1.19V,大约有土1%的误差。如果用户需要知道每一颗芯片的准确内部参考信号源值,可外接精准参考信号源,然后利用ADC的第15通道进行测量标定。ADC_VRef+脚外接参考电源时,可利用ADC的第15通道可以反推ADC_VRef+脚外接参考电源的电压;如将ADC_VREF+短接到MCU-VCC,就可以反推 MCU-VCC的电压。
    如果芯片有ADC 的外部参考电源管脚ADC_VRef+,则一定不能浮空,必须接外部参考电源或者直接连到VCC。
    假设单片机的基准电压为2.96V,以5V为例,比较结果如下:
    在这里插入图片描述
    注意:使用ADC功能时有Vref引脚的单片机千万千万千万不能悬空,必须接外部参考电压源或者VCC!!!

    2.STC32G单片机ADC使用原理

    19.1.1 ADC控制寄存器(ADC_CONTR)

    在这里插入图片描述
    在这里插入图片描述
    只有15个引脚,可以使用单片机的ADC,不是所有引脚都能使用ADC功能,只有指定的这个ADC的通道(1.19V参考),这15个才能进行ADC的一个转化。
    ADC_EPWMT:使能PWM实时触发ADC功能。详情请参考16位高级PWM定时器章节,本节暂时略过,不详细探讨。
    如ADC_CHS3:0写0000,就可以使用P1.0。

    19.1.2 ADC配置寄存器(ADCCFG)

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    时钟建议选一个慢一点的时钟,设置的时间可以看{FADC=SYSclk/2/(SPEED+1)},不继续深入。实际上ADC大部分使用的情况都和时间没有太大影响。
    时钟选择手册中的默认值就可以。

    19.1.4ADC时序控制寄存器(ADCTIM)

    在这里插入图片描述
    一般建议均使用默认值。(注意:SMPDUTY一定不能设置小于01010B),即从参数图上看,建议往下不要再往上走。
    12位ADC的转换时间固定为12个ADC工作时钟。
    一个完整的ADC转换时间为:Tsetup+Tduty+ Thold+Tconvert、如下图所示:
    在这里插入图片描述

    19.3 ADC相关计算公式

    19.3.1 ADC速度计算公式本次不涉及。
    在这里插入图片描述
    在这里插入图片描述

    19.4.1 一般精度ADC参考线路图
    在这里插入图片描述
    19.4.2高精度ADC参考线路图
    在这里插入图片描述
    两者的主要区别在于VREF的处理,高精度的ADC有独立的基准2.5V电压源。
    不建议使用串口电路,易受供电电压波动影响。
    建议使用:ISP下载典型应用线路图中的USB接线电路,比串口更方便,更实用。
    在这里插入图片描述
    在这里插入图片描述

    3.编写最简单的ADC采集代码(查询&中断)

    官方查询方式例程为:
    在这里插入图片描述
    在这里插入图片描述

    P1M0= 0x00;P1M1 = 0x01; //将IO口P1.0设置为高阻状态。
    可以在STC-ISP中找到相应设置,选择接口和拟设置的端口模式,自动生成代码,可以直接复制。
    在这里插入图片描述

    三个主要的寄存器配置:

    ADCTIM= 0x3f;   //设置ADC内部时序  0x3f=0011 1111
    ADCCFG= 0x0f;    //设置ADC时钟为系统时钟/2/16/16
    ADC_POWER = 1;    //使能ADC模块
    
    • 1
    • 2
    • 3

    编写代码之前,需要看一下原理图(实验箱9.6_2022-12-05-SCH)上,我们的芯片ADC使用哪个引脚:
    在这里插入图片描述

    P10的引脚去获取一个ADC代码

    以上节的12.IO中断为模板,复制并修改为13.ADC模拟电压采集.
    \HARDWARE文件夹下新建ADC子目录,并新建adc.c和adc.h,把ADCH添加进我们的路径里。添加.h文件模板,在.c和主文件内引用。
    我们把它我们的ADC也先初始化一下,添加函数声明及定义。
    adc.h为:

    #ifndef __ADC_H
    #define __ADC_H
    
    #include "COMM/stc.h"			//调用头文件
    #include "COMM/usb.h"
    
    //------------------------引脚定义------------------------//
    
    
    //------------------------变量声明------------------------//
    
    
    //------------------------函数声明-----------------------//
    void ADC_Init(void); 	//ADC初始化
    u16 ADC_Read(u8 no );	//ADC读取指定通道的adc电压
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    adc.c为:

    #include "adc.h"
    #include "intrins.h"
    
    //========================================================================
    // 函数名称:ADC_Init
    // 函数功能:ADC初始化
    // 入口参数:无
    // 函数返回:无
    // 当前版本: VER1.0
    // 修改日期: 2023
    // 当前作者:
    // 其他备注:
    //========================================================================
    void ADC_Init(void)		//ADC初始化
    {
    	P1M0 = 0x00;	//设置P1.0引脚为高阻输入,参考点亮LED章节
    	P1M1 = 0x01;
    
    	ADCTIM= 0x3f;   //设置ADC内部时序  0x3f=0011 1111
    	ADCCFG= 0x2f;   //设置ADC为数据右对齐。时钟为系统时钟/2/16/16  0x2f=0010 1111
    	ADC_POWER = 1;  //使能ADC模块
    }
    
    //========================================================================
    // 函数名称:ADC_Read
    // 函数功能:读取指定通道的adc电压
    // 入口参数: @no:通道0-15
    // 函数返回:当前的12位adc数值
    // 当前版本: VER1.0
    // 修改日期: 2023
    // 当前作者:
    // 其他备注:
    //========================================================================
    
    u16 ADC_Read(u8 no)						//读取指定通道的adc电压
    {
    	u16 adcval;							//adc数值保存变量
    	ADC_CONTR &= 0xf0;					//清空通道,要保持它的低4位为0
    	ADC_CONTR |= no;					//选择通道
    	ADC_START = 1;						//开启ADC通道
    	_nop_();
    	_nop_();							//空操作指令,比delay远远短
    	while(!ADC_FLAG);					//等待转换结束。ADC_FLAG:ADC转换结束标志位。当ADC完成一次转换后,硬件会自动将此位置1,并向CPU提出中断请求。
    	ADC_FLAG = 0;						//此标志位必须软件清零。
    	adcval = (ADC_RES << 8) + ADC_RESL;	//计算adc的数值,我们这边给它右对齐一下(最高4位恒定是0)
    	return adcval;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在主函数里面,去循环的读取那个adc的数值。新建u16变量,保存adc的数值:u16 ADC_VAL; //ADC的数值
    demo.c:

    #include "COMM/stc.h"		//调用头文件
    #include "COMM/usb.h"
    #include "seg_led.h"
    #include "key.h"			//调用头文件
    #include "beep.h"
    #include "tim0.h"
    #include "exit.h"
    #include "adc.h"
    
    
    #define MAIN_Fosc 24000000UL	//定义主时钟
    
    char *USER_DEVICEDESC = NULL;
    char *USER_PRODUCTDESC = NULL;
    char *USER_STCISPCMD = "@STCISP#";
    
    bit TIM_10MS_Flag;		//10ms标志位
    
    void sys_init();	//函数声明
    void delay_ms(u16 ms);
    
    void Timer0_Isr(void);
    
    u16 Time_CountDown = 0;	//全局变量,文件里所有地方都可以调用 大于255定义u16
    
    void main()					//程序开始运行的入口
    {
    	u16 ADC_VAL;			//ADC的数值
    	sys_init();				//USB功能+IO口初始化
    	usb_init();				//usb库初始化
    	Timer0_Init();			//定时器0初始化
    
    	ADC_Init();
    
    	EA = 1;					//CPU开放中断,打开总中断。
    
    	while(1)		//死循环
    	{
    		delay_ms(2);								//让USB稳定下来
    //		if( DeviceState != DEVSTATE_CONFIGURED ) 	//
    //			continue;
    		if( bUsbOutReady )
    		{
    			usb_OUT_done();
    		}
    		if(TIM_10MS_Flag == 1)   					//将需要延时的代码部分放入
    		{
    			TIM_10MS_Flag = 0;						//TIM_10MS_Flag 变量清空置位
    		}
    		ADC_VAL = ADC_Read(0);						//保存ADC的数值,使用P10,即取0
    		printf("当前ADC数\XFD值:%d\r\n",(int)ADC_VAL); //打印ADC的数值,直接打印会出乱码,数后面需要加\XFD
    	}
    }
    
    void sys_init()		//函数定义
    {
        WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        EAXFR = 1; //扩展寄存器(XFR)访问使能
        CKCON = 0; //提高访问XRAM速度
    
    	P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
        P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
        P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
        P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
        P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
        P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
        P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
        P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
    
        P3M0 = 0x00;
        P3M1 = 0x00;
    
        P3M0 &= ~0x03;
        P3M1 |= 0x03;
    
        //设置USB使用的时钟源
        IRC48MCR = 0x80;    //使能内部48M高速IRC
        while (!(IRC48MCR & 0x01));  //等待时钟稳定
    
        USBCLK = 0x00;	//使用CDC功能需要使用这两行,HID功能禁用这两行。
        USBCON = 0x90;
    }
    
    
    void delay_ms(u16 ms)	//unsigned int
    {
    	u16 i;
    	do
    	{
    		i = MAIN_Fosc/6000;
    		while(--i);
    	}while(--ms);
    }
    
    void Timer0_Isr(void) interrupt 1 		//1ms进来执行一次,无需其他延时,重复赋值
    {
    	static timecount = 0;
    
    	SEG_LED_Show();		//数码管刷新
    
    	timecount++;		//1ms+1
    	if(timecount>=10)	//如果这个变量大于等于10,说明10ms到达
    	{
    		timecount = 0;
    		TIM_10MS_Flag = 1;	//10ms到了
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    编译下载,串口持续打印0,1。
    根据原理图,可以按下按键改变adc的值。按下按键,得到的打印结果与原理图一致。
    这里使用的源为高精度基准电压2.5V,默认焊接R79,如图:
    在这里插入图片描述

    也可以用万用表的电压档,测量ADC的实际基准电压,红正,黑接地。

    表上显示2.498伏非常的稳定,这个就是基准电压源。

    再测一下ADC的数值,测量R27的电阻边上,测量得到读数与原理图可以对照。
    也可以用计算器来换算一下,看看电压是否正确:
    在这里插入图片描述

    可以根据这个反推出一个(引脚上的)电源电压。
    增加函数:u16 ADC_CAL_Voltage(u16 num)

    u16 ADC_CAL_Voltage(u16 num)
    {
    	return num*2.5*1000/4096;
    }
    
    • 1
    • 2
    • 3
    • 4

    将读取和打印代码移入延时代码段内,实现10ms检测打印1次:

    		if(TIM_10MS_Flag == 1)   					//将需要延时的代码部分放入
    		{
    			TIM_10MS_Flag = 0;						//TIM_10MS_Flag 变量清空置位
    			ADC_VAL = ADC_Read(0);						//保存ADC的数值,使用P10,即取0
    			printf("当前ADC数\xfd值:%d\t%d\r\n",(int)ADC_VAL,ADC_CAL_Voltage(ADC_VAL)); //打印ADC的数值,直接打印会出乱码,数后面需要加\XFD}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    编译,下载。不点击按钮时,显示0,按下按钮,显示256,156mv。

    中断方式ADC的实现

    只是多了一个EADC的这个操作:EADC=1;
    我们如果需要在中断里面一直循环,那我们就一直开启。增加void ADC_Init(void)和void ADC_Isr() interrupt 5:

    void ADC_Init(void)		//ADC初始化
    {
    	P1M0 = 0x00;	    //设置P1.0引脚为高阻输入,参考点亮LED章节
    	P1M1 = 0x01;
    
    	ADCTIM= 0x3f;       //设置ADC内部时序  0x3f=0011 1111
    	ADCCFG= 0x2f;       //设置ADC为数据右对齐。时钟为系统时钟/2/16/16  0x2f=0010 1111
    	ADC_POWER = 1;      //使能ADC模块
    	EADC = 1;		    //开启中断模式
    }
    
    void ADC_Isr() interrupt 5
    {
    	ADC_FLAG = 0;							//此标志位必须软件清零,清空读取标志位
    	adc_val =  = (ADC_RES << 8) + ADC_RESL;	//读取adc的数值,我们这边给它右对齐一下(最高4位恒定是0)
    	ADC_START = 1;							//开启ADC通道
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.编写最简单的ADC采集代码

    这里有一个问题,ADC_Init查询模式和中断模式都进行了定义且同名,编译会出现错误。需要采用条件编译来规避。

    HARDWARE\ADC\adc.c(75): error C53: redefinition of 'ADC_Init': function already defined
    
    • 1

    引入if预编译模板:

    #define ADC_CHECK  0 	    //查询
    #define ADC_Isr          1 	//中断
    
    #define  ADC_Func  ADC_CHECK
    
    #if ADC_Func == ADC_CHECK
    //adc查询的相关定义
    #elif ADC_Func == ADC_Isr
    //adc中断的相关定义
    #else
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    将模板插入adc.h,并修改:

    #ifndef __ADC_H
    #define __ADC_H
    
    #include "COMM/stc.h"			//调用头文件
    #include "COMM/usb.h"
    
    #define ADC_CHECK  0 	   	 	//查询
    #define ADC_Isr    1 			//中断
    
    #define  ADC_Func  ADC_CHECK	//最终选择
    
    #if ADC_Func == ADC_CHECK
    	//adc查询的相关定义
    #elif ADC_Func == ADC_Isr
    	//adc中断的相关定义
    #else
    
    #endif
    
    //------------------------引脚定义------------------------//
    
    
    //------------------------变量声明------------------------//
    
    extern u16 adc_val;			//中断获取到的ADC数值
    //------------------------函数声明-----------------------//
    void ADC_Init(void); 	//ADC初始化
    u16 ADC_Read(u8 no );	//ADC读取指定通道的adc电压
    u16 ADC_CAL_Voltage(u16 num);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    adc的这个数值非常重要,比如说报警的时候,假设我们检测外部有没有着火。
    当这个adc的数值非常大(假设火越大,传感器出来的电压也越大),表示外面已经着火的时候,这个时候就要立马执行灭火,要不然的话,火势一起来就已经灭不掉火了

    编译,下载。运行时,按下按钮没有反应。
    排查:先跳转到初始化,发现ADC_Init跳转到了查询代码段,实际上要用的是中断的初始化,然后我们才可以进入。
    需要把头文件中的查询换成中断:#define ADC_Func ADC_Isr //编译选择
    修改后的代码如下:
    adc.c:

    #include "adc.h"
    #include "intrins.h"
    
    u16 adc_val;				//获取到的ADC数值
    
    #if ADC_Func == ADC_CHECK
    	//adc查询的相关定义
    	//========================================================================
    	// 函数名称:ADC_Init
    	// 函数功能:ADC初始化
    	// 入口参数:无
    	// 函数返回:无
    	// 当前版本: VER1.0
    	// 修改日期: 2023
    	// 当前作者:
    	// 其他备注:
    	//========================================================================
    	void ADC_Init(void)		//ADC初始化
    	{
    		P1M0 = 0x00;	//设置P1.0引脚为高阻输入,参考点亮LED章节
    		P1M1 = 0x01;
    
    		ADCTIM= 0x3f;   //设置ADC内部时序  0x3f=0011 1111
    		ADCCFG= 0x2f;   //设置ADC为数据右对齐。时钟为系统时钟/2/16/16  0x2f=0010 1111
    		ADC_POWER = 1;  //使能ADC模块
    	}
    
    	//========================================================================
    	// 函数名称:ADC_Read
    	// 函数功能:读取指定通道的adc电压
    	// 入口参数: @no:通道0-15
    	// 函数返回:当前的12位adc数值
    	// 当前版本: VER1.0
    	// 修改日期: 2023
    	// 当前作者:
    	// 其他备注:
    	//========================================================================
    
    
    	u16 ADC_Read(u8 no)						//读取指定通道的adc电压
    	{
    		u16 adcval;							//adc数值保存变量
    		ADC_CONTR &= 0xf0;					//清空通道,要保持它的低4位为0
    		ADC_CONTR |= no;					//选择通道
    		ADC_START = 1;						//开启ADC通道
    		_nop_();
    		_nop_();							//空操作指令,比delay远远短
    		while(!ADC_FLAG);					//等待转换结束。ADC_FLAG:ADC转换结束标志位。当ADC完成一次转换后,硬件会自动将此位置1,并向CPU提出中断请求。
    		ADC_FLAG = 0;						//此标志位必须软件清零。
    		adcval = (ADC_RES << 8) + ADC_RESL;	//计算adc的数值,我们这边给它右对齐一下(最高4位恒定是0)
    		return adcval;
    	}
    
    #elif ADC_Func == ADC_Isr
    		//adc中断的相关定义
    	//========================================================================
    	// 函数名称:ADC_Init
    	// 函数功能:中断的ADC初始化
    	// 入口参数:无
    	// 函数返回:无
    	// 当前版本: VER1.0
    	// 修改日期: 2023
    	// 当前作者:
    	// 其他备注:
    	//========================================================================
    
    	void ADC_Init(void)		//ADC初始化
    	{
    		P1M0 = 0x00;		//设置P1.0引脚为高阻输入,参考点亮LED章节
    		P1M1 = 0x01;
    
    		ADCTIM= 0x3f;   	//设置ADC内部时序  0x3f=0011 1111
    		ADCCFG= 0x2f;   	//设置ADC为数据右对齐。时钟为系统时钟/2/16/16  0x2f=0010 1111
    		ADC_POWER = 1;  	//使能ADC模块
    		EADC = 1;			//打开中断
    		ADC_START = 1;		//开启ADC通道
    	}
    	//========================================================================
    	// 函数名称:ADC_iSR
    	// 函数功能:
    	// 入口参数: @
    	// 函数返回:
    	// 当前版本: VER1.0
    	// 修改日期: 2023
    	// 当前作者:
    	// 其他备注:
    	//========================================================================
    
    	void ADC_iSR()interrupt 5					//这里不能使用ADC_Isr(),会与预定义“#define ADC_Isr    1  //中断 ”混淆
    	{
    		ADC_FLAG = 0;							//此标志位必须软件清零,清空读取标志位
    		adc_val = (ADC_RES << 8) + ADC_RESL;	//读取adc的数值,我们这边给它右对齐一下(最高4位恒定是0)
    		ADC_START = 1;							//开启ADC通道
    	}
    
    #else
    
    #endif
    
    //========================================================================
    // 函数名称:ADC_CAL_Voltage
    // 函数功能:将ADC数值换算成电源电压
    // 入口参数: @num:ADC的数值,取值范围0-4095
    // 函数返回:电源电压 单位mv
    // 当前版本: VER1.0
    // 修改日期: 2023
    // 当前作者:
    // 其他备注:
    //========================================================================
    u16 ADC_CAL_Voltage(u16 num)
    {
    	return num*2.5*1000/4096;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    adc.h:

    #ifndef __ADC_H
    #define __ADC_H
    
    #include "COMM/stc.h"			//调用头文件
    #include "COMM/usb.h"
    
    #define ADC_CHECK  0 	   	 	//查询
    #define ADC_Isr    1 			//中断
    
    #define  ADC_Func  ADC_Isr		//编译选择
    
    
    //------------------------引脚定义------------------------//
    
    
    //------------------------变量声明------------------------//
    
    extern u16 adc_val;				//中断获取到的ADC数值
    //------------------------函数声明-----------------------//
    void ADC_Init(void);	//ADC初始化
    u16 ADC_Read(u8 no);	//ADC读取指定通道的adc电压
    u16 ADC_CAL_Voltage(u16 num);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    demo.c:

    #include "COMM/stc.h"		//调用头文件
    #include "COMM/usb.h"
    #include "seg_led.h"
    #include "key.h"			//调用头文件
    #include "beep.h"
    #include "tim0.h"
    #include "exit.h"
    #include "adc.h"
    
    
    #define MAIN_Fosc 24000000UL	//定义主时钟
    
    char *USER_DEVICEDESC = NULL;
    char *USER_PRODUCTDESC = NULL;
    char *USER_STCISPCMD = "@STCISP#";
    
    bit TIM_10MS_Flag;		//10ms标志位
    
    void sys_init();	//函数声明
    void delay_ms(u16 ms);
    
    void Timer0_Isr(void);
    
    u16 Time_CountDown = 0;	//全局变量,文件里所有地方都可以调用 大于255定义u16
    
    void main()					//程序开始运行的入口
    {
    	//u16 ADC_VAL;			//ADC的数值
    	sys_init();				//USB功能+IO口初始化
    	usb_init();				//usb库初始化
    	Timer0_Init();			//定时器0初始化
    
    	ADC_Init();
    
    	EA = 1;					//CPU开放中断,打开总中断。
    
    	while(1)		//死循环
    	{
    		delay_ms(2);								//让USB稳定下来
    //		if( DeviceState != DEVSTATE_CONFIGURED ) 	//
    //			continue;
    		if( bUsbOutReady )
    		{
    			usb_OUT_done();
    		}
    		if(TIM_10MS_Flag == 1)   					//将需要延时的代码部分放入
    		{
    			TIM_10MS_Flag = 0;						//TIM_10MS_Flag 变量清空置位
    
    			//ADC_VAL = ADC_Read(0);						//保存ADC的数值,使用P10,即取0
    
    			printf("当前ADC数\xfd值:%d\t%dmv\r\n",(int)adc_val,ADC_CAL_Voltage(adc_val)); //打印ADC的数值,直接打印会出乱码,数后面需要加\XFD,单位mv
    		}
    	}
    }
    
    void sys_init()		//函数定义
    {
        WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        EAXFR = 1; //扩展寄存器(XFR)访问使能
        CKCON = 0; //提高访问XRAM速度
    
    	P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
        P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
        P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
        P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
        P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
        P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
        P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
        P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
    
        P3M0 = 0x00;
        P3M1 = 0x00;
    
        P3M0 &= ~0x03;
        P3M1 |= 0x03;
    
        //设置USB使用的时钟源
        IRC48MCR = 0x80;    //使能内部48M高速IRC
        while (!(IRC48MCR & 0x01));  //等待时钟稳定
    
        USBCLK = 0x00;	//使用CDC功能需要使用这两行,HID功能禁用这两行。
        USBCON = 0x90;
    }
    
    
    void delay_ms(u16 ms)	//unsigned int
    {
    	u16 i;
    	do
    	{
    		i = MAIN_Fosc/6000;
    		while(--i);
    	}while(--ms);
    }
    
    void Timer0_Isr(void) interrupt 1 		//1ms进来执行一次,无需其他延时,重复赋值
    {
    	static timecount = 0;
    
    	SEG_LED_Show();		//数码管刷新
    
    	timecount++;		//1ms+1
    	if(timecount>=10)	//如果这个变量大于等于10,说明10ms到达
    	{
    		timecount = 0;
    		TIM_10MS_Flag = 1;	//10ms到了
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    总结

    1.了解ADC的位数、引脚、基准电压、等关键名词。
    2.学会ADC的原理,学会用法和电源的换算公式。

    课后练习:

    简易电压表:
    1.用前4位数码管显示ADC的数值
    2.用后四位数码管显示最终电压。
    3.电压大于2.2V,蜂鸣长响,表示快要到达上限

  • 相关阅读:
    奶制品数据可视化,去年全国奶制品产量高达3778万吨,同比增长7.1%
    第三章:栈/队列 重点题
    智能电网中需求响应研究(Matlab代码实现)
    python-opencv 图像处理基础 (九)图像梯度
    站群程序-免费站群程序排名
    单片机之硬件记录
    模式识别——贝叶斯决策理论
    基于Android的手机通讯录设计
    【c++】对象与对象之间的关系
    学习太极创客 — ESP8226 (十三)OTA
  • 原文地址:https://blog.csdn.net/Medlar_CN/article/details/133080707