嵌入式开发学习(STC51-16-ADC模数转换)

news2024/10/7 20:25:16

内容

通过ADC转换电路采集电位器AD值,将采集的AD值转换成电压值,通过数码管显示出来;

采集光敏电阻的AD值;

采集热敏电阻的AD值;

采集外部通道AIN3的电压值;

为了方便显示,我们可以通过独立按键k1-k4来切换显示哪种数据;(k1:电位器电压;k2:外部通道AIN3电压;k3:光敏电阻AD值;k4:热敏电阻AD值)

ADC介绍

简介

ADC(analog to digital converter)也称为模数转换器,是指一个将模拟信号转变为数字信号的器件;

单片机在采集模拟信号时,通常都需要在前端加上A/D芯片;

数字量和模拟量

首先我们知道51单片机系统内部运算时用的全部是数字量,即0和1,因此对单片机系统而言,无法直接操作模拟量,必须将模拟量转换成数字量才能操作;

所谓数字量,就是用一系列0和1组成的二进制代码表示某个信号大小的量;

用数字量表示同一个模拟量时,数字位数可以多也可以少,位数越多则表示的精度越高,位数越少表示的精度就越低;

主要技术指标

分辨率

ADC的分辨率是指对于允许范围内的模拟信号,它能输出离散数字信号值的个数;

这些信号值通常用二进制数来存储,因此分辨率经常用比特作为单位,且这些离散值的个数是2的幂指数;

例如:12位ADC的分辨率就是12位,或者说分辨率为满刻度的1/(2^12);

一个10V满刻度的12位ADC能分辨输入电压变化最小值是10V×1/(2^12)=2.4mV;

转换误差

转换误差通常是以输出误差的最大值形式给出,它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别,常用最低有效位的倍数表示;

例如给出相对误差≤±LSB/2,这就表明实际输出的数字量和理论上应得到的输出数字量之间的误差小于最低位的半个字;

转换速率

ADC的转换速率是能够重复进行数据转换的速度,即每秒转换的次数;

完成一次A/D转换所需的时间(包括稳定时间),则是转换速率的倒数;

ADC转换原理

AD转换器(ADC)将模拟量转换为数字量通常要经过4个步骤:采样、保持、量化和编码;

采样:所谓采样即是将一个时间上连续变化的模拟量转换为时间上离散变化的模拟量;

如下图所示:
在这里插入图片描述
保持:将采样结果存储起来,直到下次采样,这个过程叫做保持;一般采样器和保持电路一起总称为采样保持电路;

量化:将采样电平归化为与之接近的离散数字电平,这个过程叫做量化;

编码:将量化后的结果按照一定数制形式表示就是编码;

ADC转换方法

将采样电平(模拟值)转换为数字值时,主要有两类方法:直接比较型与间接比较型;

直接比较型

就是将输入模拟信号直接与标准的参考电压比较,从而得到数字量;

常见的有并行ADC和逐次比较型ADC;

间接比较型

输入模拟量不是直接与参考电压比较,而是将二者变为中间的某种物理量在进行比较,然后将比较所得的结果进行数字编码;

常见的有双积分型ADC;

常用方法

比较常用的为逐次比较型ADC和双积分型ADC:

逐次比较型ADC

采用逐次比较法的AD转换器是有一个比较器、DA 转换器、缓冲寄存器和控制逻辑电路组成;

如下图所示:
在这里插入图片描述
基本原理是:从高位到低位逐次试探比较,就像用天平秤物体,从重到轻逐级增减砝码进行试探;

逐次比较法的转换过程是:
1 初始化时将逐次比较寄存器各位清零,转换开始时,先将逐次比较寄存器最高位置1,送入DA转换器,经DA转换后生成的模拟量送入比较器,称为U0,与送入比较器的待转换的模拟量Ux进行比较,若U0 < Ux,该位1被保留,否则被清除;
2 然后再将逐次比较寄存器次高位置1,将寄存器中新的数字量送DA转换器,输出的U0再与Ux比较,若U0 < Ux,该位1被保留,否则被清除;
3 重复此过程,直至比较寄存器最低位;转换结束后,将逐次比较寄存器中的数字量送入缓冲寄存器,得到数字量的输出;
4 逐次比较的操作过程是在一个控制电路的控制下进行的;

双积分型ADC

采用双积分法的AD转换器由电子开关、积分器、比较器和控制逻辑等部件组成;

如下图所示:
在这里插入图片描述

基本原理是:将输入电压变换成与其平均值成正比的时间间隔,再把此时间间隔转换成数字量,属于间接转换;

双积分法AD转换的过程是:
1 先将开关接通待转换的模拟量Vi,Vi采样输入到积分器,积分器从零开始进行固定时间T的正向积分,时间T到后,开关再接通与Vi极性相反的基准电压Vref,将Vref输入到积分器,进行反向积分,直到输出为0V时停止积分;
2 Vi越大,积分器输出电压越大,反向积分时间也越长;
3 计数器在反向积分时间内所计的数值,就是输入模拟电压Vi所对应的数字量,实现了AD转换;

XPT2046芯片介绍

简介

XPT2046是一款4线制电阻式触摸屏控制器,内含12位分辨率、125KHz转换速率、逐步比较型的A/D转换器;

XPT2046支持从1.5V到5.25V的低电压I/O接口;

XPT2046能通过执行两次A/D转换查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力;

内部自带2.5V参考电压,可以作为辅助输入、温度测量和电池监测之用,电池监测的电压范围可以从0V到6V;

XPT2046片内集成有一个温度传感器;

在2.7V的典型工作状态下,关闭参考电压,功耗可小于0.75mW;

工作温度范围为-40℃~+85℃,与ADS7846、TSC2046、AK4182A完全兼容;

主要特性

  • 工作电压范围为1.5V~5.25V
  • 支持1.5V~5.25V的数字 I/O 口
  • 内建2.5V参考电压源
  • 电源电压测量(0V~6V)
  • 内建结温测量功能
  • 触摸压力测量
  • 采用3线制SPI通信接口
  • 具有自动省电功能

芯片管脚说明

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
XPT2046是一种典型的逐次比较型模数转换器(SAR ADC),包含了采样/保持、模数转换、串口数据、输出等功能;

同时芯片集成有一个2.5V的内部参考电压源、温度检测电路,工作时使用外部时钟;

XPT2046可以单电源供电,电源电压范围为2.7V~5.5V;

参考电压值直接决定ADC的输入范围,参考电压可以使用内部参考电压,也可以从外部直接输入1V~VCC范围内的参考电压(要求外部参考电压源输出阻抗低);

模式控制

X、Y、Z、VBAT、Temp和AUX模拟信号经过片内的控制寄存器选择后进入ADC,ADC可以配置为单端或差分模式;

选择VBAT、Temp和AUX时应该配置为单端模式;作为触摸屏应用时,应该配置为差分模式,这可有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换精度;

单端和差分模式输入配置如下图所示:
在这里插入图片描述

命令格式

在对XPT2046进行控制时,控制字节由DIN输入的控制;

命令格式如下所示:
在这里插入图片描述
在这里插入图片描述

工作时序(SPI读写时序)

(SPI读写时序)

XPT2046数据接口是串行接口,其典型工作时序如下图所示:(图中展示的信号来自带有基本串行接口的单片机或数据信号处理器)
在这里插入图片描述
处理器和转换器之间的的通信需要8个时钟周期,可采用SPI、SSI和Microwire等同步串行接口;

一次完整的转换需要24个串行同步时钟(DCLK)来完成;

前8个时钟用来通过DIN引脚输入控制字节;

当转换器获取有关下一次转换的足够信息后,接着根据获得的信息设置输入多路选择器和参考源输入,并进入采样模式,如果需要,将启动触摸面板驱动器;

3个多时钟周期后,控制字节设置完成,转换器进入转换状态;这时,输入采样-保持器进入保持状态,触摸面板驱动器停止工作(单端工作模式);接着的12个时钟周期将完成真正的模数转换;

如果是度量比率转换方式(SER/DFR=0),驱动器在转换过程中将一直工作,第13个时钟将输出转换结果的最后一位;

剩下的3个多时钟周期将用来完成被转换器忽略的最后字节(DOUT置低);

原理图

在这里插入图片描述
由图可知,控制管脚DIN、CS、DCLK、DOUT分别连接单片机p34、p35、p36、p37口;

其中ADC的输入转换通道分别接入了AD1电位器(AIN0)、NTC1热敏传感器(AIN1)、GR1光敏传感器(AIN2),还有一个外接通道AIN3接在DAC(PWM)(数模转换)模块的J52端子上,供外部模拟信号检测;

采集电位器的电压值

思路

按照工作时序(SPI通信)编写读写程序;

根据模式控制的配置图和命令格式,如果要检测AD1电位器,即AIN0/X+管脚输入的模拟信号,则命令配置为(0x94=1001 0100)

得到AD值后,可按照如下公式计算电压值:Vref*ADC_Value/分辨率;

其中Vref是参考电压,XPT2046接入的是5V,ADC_Value是读取的AD值,分辨率是ADC的位数(2^12=4096);

然后将该值放大10倍,即保留小数后一位;

最后将得到的电压值转换为数码管段码数据显示;

编码

User

main.c

/*
 * @Description: 采集电位器的电压值
 */
#include "public.h"
#include "smg.h"
#include "xpt2046.h"

void main()
{
	u16 adc_value = 0;
	float adc_vol; // ADC电压值
	u8 adc_buf[3];

	while (1)
	{
		adc_value = xpt2046_read_adc_value(0x94); // 测量电位器
		adc_vol = 5.0 * adc_value / 4096;		  // 将读取的AD值转换为电压
		adc_value = adc_vol * 10;				  // 放大10倍,即保留小数点后一位
		adc_buf[0] = gsmg_code[adc_value / 10] | 0x80;
		adc_buf[1] = gsmg_code[adc_value % 10];
		adc_buf[2] = 0x3e; // 显示单位V
		smg_display(adc_buf, 6);
	}
}

Public

public.h

#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;

void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif

public.c

#include "public.h"

/**
 * @description: 延时函数,ten_us=1时,大约延时10us
 * @param {u16} ten_us 延时倍数
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**ms延时函数,ms=1时,大约延时1ms***
 * @param {u16} ms 延时倍数
 * @return {*}
 */
void delay_ms(u16 ms)
{
	u16 i, j;
	for (i = ms; i > 0; i--)
		for (j = 110; j > 0; j--)
			;
}

App/xpt2046

xpt2046.h

#ifndef _xpt2046_H
#define _xpt2046_H

#include "public.h"

// 管脚定义
sbit DOUT = P3 ^ 7; // 输出
sbit CLK = P3 ^ 6;	// 时钟
sbit DIN = P3 ^ 4;	// 输入
sbit CS = P3 ^ 5;	// 片选

// 函数声明
u16 xpt2046_read_adc_value(u8 cmd);

#endif

xpt2046.c

#include "xpt2046.h"
#include "intrins.h"

/**
 * @description: XPT2046写数据
 * @param {u8} dat 要写入的数据
 * @return {*}
 */
void xpt2046_wirte_data(u8 dat)
{
	u8 i;

	CLK = 0;
	_nop_();
	for (i = 0; i < 8; i++) // 循环8次,每次传输一位,共一个字节
	{
		DIN = dat >> 7; // 先传高位再传低位
		dat <<= 1;		// 将低位移到高位
		CLK = 0;		// CLK由低到高产生一个上升沿,从而写入数据
		_nop_();
		CLK = 1;
		_nop_();
	}
}

/**
 * @description: XPT2046读数据
 * @return {u16} XPT2046返回12位数据
 */
u16 xpt2046_read_data(void)
{
	u8 i;
	u16 dat = 0;

	CLK = 0;
	_nop_();
	for (i = 0; i < 12; i++) // 循环12次,每次读取一位,大于一个字节数,所以返回值类型是u16
	{
		dat <<= 1;
		CLK = 1;
		_nop_();
		CLK = 0; // CLK由高到低产生一个下降沿,从而读取数据
		_nop_();
		dat |= DOUT; // 先读取高位,再读取低位
	}
	return dat;
}

/**
 * @description: XPT2046读AD数据
 * @param {u8} cmd 命令
 * @return {u16} XPT2046的返回AD值
 */
u16 xpt2046_read_adc_value(u8 cmd)
{
	u8 i;
	u16 adc_value = 0;

	CLK = 0;				 // 先拉低时钟
	CS = 0;					 // 使能XPT2046
	xpt2046_wirte_data(cmd); // 发送命令字
	for (i = 6; i > 0; i--)
		; // 延时等待转换结果
	CLK = 1;
	_nop_();
	CLK = 0; // 发送一个时钟,清除BUSY
	_nop_();
	adc_value = xpt2046_read_data();
	CS = 1; // 关闭XPT2046
	return adc_value;
}

App/smg

smg.h

#ifndef _smg_H
#define _smg_H

#include "public.h"

#define SMG_A_DP_PORT P0 // 使用宏定义数码管段码口

// 定义数码管位选信号控制脚
sbit LSA = P2 ^ 2;
sbit LSB = P2 ^ 3;
sbit LSC = P2 ^ 4;

extern u8 gsmg_code[17]; // 使“共阴极数码管显示0~F的段码数据”这个变量定义为外部可用

void smg_display(u8 dat[], u8 pos);

#endif

smg.c

#include "smg.h"

// 共阴极数码管显示0~F的段码数据
u8 gsmg_code[17] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

/**
 * @description: 动态数码管显示函数
 * @param {u8} dat 要显示的数据
 * @param {u8} pos 从左开始第几个位置开始显示,范围1-8
 * @return {*}
 */
void smg_display(u8 dat[], u8 pos)
{
	u8 i = 0;
	u8 pos_temp = pos - 1;

	for (i = pos_temp; i < 8; i++)
	{
		switch (i) // 位选
		{
		case 0:
			LSC = 1;
			LSB = 1;
			LSA = 1;
			break;
		case 1:
			LSC = 1;
			LSB = 1;
			LSA = 0;
			break;
		case 2:
			LSC = 1;
			LSB = 0;
			LSA = 1;
			break;
		case 3:
			LSC = 1;
			LSB = 0;
			LSA = 0;
			break;
		case 4:
			LSC = 0;
			LSB = 1;
			LSA = 1;
			break;
		case 5:
			LSC = 0;
			LSB = 1;
			LSA = 0;
			break;
		case 6:
			LSC = 0;
			LSB = 0;
			LSA = 1;
			break;
		case 7:
			LSC = 0;
			LSB = 0;
			LSA = 0;
			break;
		}
		SMG_A_DP_PORT = dat[i - pos_temp]; // 传送段选数据
		delay_10us(100);							  // 延时一段时间,等待显示稳定
		SMG_A_DP_PORT = 0x00;						  // 消影
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:成功采集电位器电压值并显示出来;
在这里插入图片描述

采集光敏电阻的AD值

思路

与采集电位器电压类似,不过不需要转换AD值了,输入口不同,所以命令也不同,为0xA4

编码

只有main.c文件不同

User

main.c

/*
 * @Description: 采集电位器的AD值
 */
#include "public.h"
#include "smg.h"
#include "xpt2046.h"

void main()
{
	u16 adc_value = 0;
	u8 adc_buf[4];

	while (1)
	{
		adc_value = xpt2046_read_adc_value(0xA4); // 测量光敏电阻
		adc_buf[0] = gsmg_code[adc_value / 1000];
		adc_buf[1] = gsmg_code[adc_value % 1000 / 100];
		adc_buf[2] = gsmg_code[adc_value % 1000 % 100 / 10];
		adc_buf[3] = gsmg_code[adc_value % 1000 % 100 % 10];
		smg_display(adc_buf, 5);
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:成功采集光敏电阻的AD值
在这里插入图片描述

采集采集热敏电阻的AD值

思路

与采集光敏电阻AD值类似,命令为0xD4

编码

只有main.c文件不同

User

main.c

/*
 * @Description: 采集热敏电阻的AD值
 */
#include "public.h"
#include "smg.h"
#include "xpt2046.h"

void main()
{
	u16 adc_value = 0;
	u8 adc_buf[4];

	while (1)
	{
		adc_value = xpt2046_read_adc_value(0xD4); // 测量热敏电阻
		adc_buf[0] = gsmg_code[adc_value / 1000];
		adc_buf[1] = gsmg_code[adc_value % 1000 / 100];
		adc_buf[2] = gsmg_code[adc_value % 1000 % 100 / 10];
		adc_buf[3] = gsmg_code[adc_value % 1000 % 100 % 10];
		smg_display(adc_buf, 5);
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:成功采集热敏电阻的AD值
在这里插入图片描述

采集外部通道AIN3的电压值

思路

与采集电位器电压值类似,命令为0xE4

编码

只有main.c文件不同

User

main.c

/*
 * @Description: 采集外部通道AIN3的电压值
 */
#include "public.h"
#include "smg.h"
#include "xpt2046.h"

void main()
{
	u16 adc_value = 0;
	float adc_vol; // ADC电压值
	u8 adc_buf[3];

	while (1)
	{
		adc_value = xpt2046_read_adc_value(0xE4); // 测量外部输入通道AIN3
		adc_vol = 5.0 * adc_value / 4096;		  // 将读取的AD值转换为电压
		adc_value = adc_vol * 10;				  // 放大10倍,即保留小数点后一位
		adc_buf[0] = gsmg_code[adc_value / 10] | 0x80;
		adc_buf[1] = gsmg_code[adc_value % 10];
		adc_buf[2] = 0x3e; // 显示单位V
		smg_display(adc_buf, 6);
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:成功采集外部通道AIN3的电压值
在这里插入图片描述

使用独立按键来切换显示内容

思路

我们只需要让按键按下时切换显示内容,因此我们需要中断函数来实现需求;

我这里选择外部中断0实现,下降沿触发方式,在中断函数里实现切换的功能;

定义静态全局变量key,确保能记录上次按下的值;

因为我们需要使用k3键连接的p32端口来实现外部中断,所以需要在中断函数里先消除k3键的影响;

由于进入中断函数,数码管控制程序可能来不及消影,所以要在中断函数再次消影确保显示正确;

退出中断函数时也要消除k3键的影响,否则可能会连续进入中断函数;

编码

额外加上独立按键检测程序;

User

main.c

/*
 * @Description: 使用独立按键来切换显示内容:先按下k3(相当于设置键),再按下k1-k4,显示需要的内容
 */
#include "public.h"
#include "smg.h"
#include "key.h"
#include "xpt2046.h"

static u8 key = KEY1_PRESS; // 定义全局变量,记录上次按下按键key的值,默认为key1的值

/**
 * @description: 外部中断0配置函数
 * @return {*}
 */
void exti0_init(void)
{
	IT0 = 1; // 跳变沿触发方式(下降沿)
	EX0 = 1; // 打开INT0的中断允许
	EA = 1;	 // 打开总中断
}

/**
 * @description: 外部中断0中断函数
 * @return {*}
 */
void exti0() interrupt 0 // 中断号必须对应上
{
	delay_10us(1000); // 消抖
	if (KEY3 == 0)	  // 再次判断K3键是否按下
	{
		SMG_A_DP_PORT = 0x00; // 数码管消影
		delay_ms(300);		  // 等待0.3s,消除key3的影响
		while (1)			  // 设置死循环,只要没按键按下就一直循环
		{
			key = key_scan(1);
			delay_10us(1000); // 消抖
			if (key)
			{
				delay_ms(300); // 等待0.3s,消除按下按键的影响
				return;		   // 确认有按键按下后就返回,继续执行主函数
			}
		}
	}
}

void main()
{
	u16 adc_value = 0;
	float adc_vol; // ADC电压值
	u8 adc_buf[4];

	exti0_init(); // 外部中断0配置

	while (1)
	{
		switch (key)
		{
		case KEY1_PRESS:
		{
			adc_value = xpt2046_read_adc_value(0x94); // 测量电位器
			adc_vol = 5.0 * adc_value / 4096;		  // 将读取的AD值转换为电压
			adc_value = adc_vol * 10;				  // 放大10倍,即保留小数点后一位
			adc_buf[0] = gsmg_code[adc_value / 10] | 0x80;
			adc_buf[1] = gsmg_code[adc_value % 10];
			adc_buf[2] = 0x3e; // 显示单位V
			smg_display(adc_buf, 6);
		}
		break;
		case KEY2_PRESS:
		{
			adc_value = xpt2046_read_adc_value(0xE4); // 测量外部输入通道AIN3
			adc_vol = 5.0 * adc_value / 4096;		  // 将读取的AD值转换为电压
			adc_value = adc_vol * 10;				  // 放大10倍,即保留小数点后一位
			adc_buf[0] = gsmg_code[adc_value / 10] | 0x80;
			adc_buf[1] = gsmg_code[adc_value % 10];
			adc_buf[2] = 0x3e; // 显示单位V
			smg_display(adc_buf, 6);
		}
		break;
		case KEY3_PRESS:
		{
			adc_value = xpt2046_read_adc_value(0xA4); // 测量光敏电阻
			adc_buf[0] = gsmg_code[adc_value / 1000];
			adc_buf[1] = gsmg_code[adc_value % 1000 / 100];
			adc_buf[2] = gsmg_code[adc_value % 1000 % 100 / 10];
			adc_buf[3] = gsmg_code[adc_value % 1000 % 100 % 10];
			smg_display(adc_buf, 5);
		}
		break;
		case KEY4_PRESS:
		{
			adc_value = xpt2046_read_adc_value(0xD4); // 测量热敏电阻
			adc_buf[0] = gsmg_code[adc_value / 1000];
			adc_buf[1] = gsmg_code[adc_value % 1000 / 100];
			adc_buf[2] = gsmg_code[adc_value % 1000 % 100 / 10];
			adc_buf[3] = gsmg_code[adc_value % 1000 % 100 % 10];
			smg_display(adc_buf, 5);
		}
		break;
		default:
		{
			adc_value = xpt2046_read_adc_value(0x94); // 测量电位器
			adc_vol = 5.0 * adc_value / 4096;		  // 将读取的AD值转换为电压
			adc_value = adc_vol * 10;				  // 放大10倍,即保留小数点后一位
			adc_buf[0] = gsmg_code[adc_value / 10] | 0x80;
			adc_buf[1] = gsmg_code[adc_value % 10];
			adc_buf[2] = 0x3e; // 显示单位V
			smg_display(adc_buf, 6);
		}
		break;
		}
	}
}

App/key

key.h

#ifndef _key_H
#define _key_H

#include "public.h"

// 定义独立按键控制脚
sbit KEY1 = P3 ^ 1;
sbit KEY2 = P3 ^ 0;
sbit KEY3 = P3 ^ 2;
sbit KEY4 = P3 ^ 3;

// 使用宏定义独立按键按下的键值
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0

u8 key_scan(u8 mode);

#endif

key.c

#include "key.h"

/**
 * @description: 检测独立按键是否按下,按下则返回对应键值
 * @param {u8} mode mode=0:单次扫描按键,mode=1:连续扫描按键
 * @return {u8} k1到k5的键值1-5,0表示没有按键按下
 */
u8 key_scan(u8 mode)
{
	static u8 key = 1;

	if (mode)
		key = 1;														// 连续扫描按键
	if (key == 1 && (KEY1 == 0 || KEY2 == 0 || KEY3 == 0 || KEY4 == 0)) // 任意按键按下
	{
		delay_10us(1000); // 消抖
		key = 0;
		if (KEY1 == 0)
			return KEY1_PRESS;
		else if (KEY2 == 0)
			return KEY2_PRESS;
		else if (KEY3 == 0)
			return KEY3_PRESS;
		else if (KEY4 == 0)
			return KEY4_PRESS;
	}
	else if (KEY1 == 1 && KEY2 == 1 && KEY3 == 1 && KEY4 == 1) // 无按键按下
	{
		key = 1;
	}
	return KEY_UNPRESS;
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:默认显示电位器电压值,按下k3键后停止显示,再次按下k1-k4键切换要显示的内容;
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/846290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NeRF室内重建对比:Nerfstudio vs. Luma AI vs. Instant-NGP

十年前&#xff0c;Matterport 改变了房地产业&#xff0c;让房地产买家可以进行数字旅游。 买家可以在房产内从一个点移动到另一个点并环顾四周。 与 2D 照片库相比&#xff0c;这是一个巨大的改进。 然而&#xff0c;买家仍然被房产内的一系列问题所困扰。 推荐&#xff1a;用…

程序员必读 | 《业务架构解构与实践》

之前看书大多"不求甚解", 意会即可&#xff0c;但是找一本新书看是可遇而不可求的。回过头来&#xff0c;摘抄研究一下已经看过的&#xff0c;也是别有一番风味的。本文就是对之前一本书的摘录。 文章中各种暗淡不一的图片&#xff0c;其实是在各种环境下&#xff0c…

Java中Date方法详解

先进行专栏介绍 本专栏是自己学Java的旅途&#xff0c;纯手敲的代码&#xff0c;自己跟着黑马课程学习的&#xff0c;并加入一些自己的理解&#xff0c;对代码和笔记 进行适当修改。希望能对大家能有所帮助&#xff0c;同时也是请大家对我进行监督&#xff0c;对我写的代码进行…

耗时6个月,我做了一款干净、免费、开源的AI数据库

一、Chat2DB简介 在消失的这段时间&#xff0c;我做了一款集成了AI的数据库管理工具Chat2DB。 他是数据库也集成了AIGC的能力&#xff0c;能够将自然语言转换为SQL&#xff0c;也可以将SQL转换为自然语言&#xff0c;还可以给出SQL的优化建议&#xff0c;可以极大提升效率。 …

使用RecyclerView构建灵活的列表界面

使用RecyclerView构建灵活的列表界面 1. 引言 在现代移动应用中&#xff0c;列表界面是最常见的用户界面之一&#xff0c;它能够展示大量的数据&#xff0c;让用户可以浏览和操作。无论是社交媒体的动态流、商品展示、新闻列表还是任务清单&#xff0c;列表界面都扮演着不可或…

智慧城市规划新引擎:探秘数字孪生中的二维与三维GIS技术差异

智慧城市作为人类社会发展的新阶段&#xff0c;正日益引领着我们迈向数字化未来的时代。在智慧城市的建设过程中&#xff0c;地理信息系统&#xff08;GIS&#xff09;扮演着举足轻重的角色。而在GIS的发展中&#xff0c;二维和三维GIS作为两大核心技术&#xff0c;在城市规划与…

LeetCode 周赛 340,质数 / 前缀和 / 极大化最小值 / 最短路 / 平衡二叉树

今天讲 LeetCode 单周赛第 340 场&#xff0c;今天状态不好&#xff0c;掉了一波大分。 2614. 对角线上的质数&#xff08;Easy&#xff09; 这道题是最近第 2 次出现质数问题&#xff0c;注意 1 不是质数&#xff01; 质数判断&#xff1a;$O(n\sqrt(U))$ 2615. 等值距离和…

程序员吐槽培训班简历造假,经验包装竟拿到阿里外包26k的offer

关于程序员速成培训班的传言和八卦很多&#xff0c;近日&#xff0c;又有一个程序员发帖吐槽培训班简历造假&#xff0c;两个大四学生报了个培训班&#xff0c;竟然给包装成有三年工作经验的人&#xff0c;更离谱的是&#xff0c;竟然还拿到了阿里外包26k的offer…… 许多网友表…

数据结构---查找

&#x1f31e;欢迎来到数据结构的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff…

axios在请求错误时获取不到err.response响应问题

一般来说&#xff1a; axios.request({...// 请求config }).then(res > console.log(success,res),err > console.log(err,err) )稍微拓展一下&#xff1a; import axios,{ AxiosRequestConfig, AxiosError, AxiosResponse } from "axios"; import { getToke…

​​五、驱动 - ​声卡构成(ASOC)

文章目录 1. 硬件结构2. 数据传输流向3. ASOC结构组成4. Linux alsa音频系统框架1. 硬件结构 codec:音频编解码器,负责处理音频信息,包括 ADC、DAC、Mixer、DSP,输入输出以及音量控制等所有与音频相关的功能; 对PCM音频数据进行D/A转换:将数字信号转换为模拟信号;对Mic、…

Effective Java笔记(27)消除非受检的警告

用泛型编程时会遇到讲多编译器警告 &#xff1a; 非受检转换警告&#xff08; unchecked cast warning &#xff09;、非受检方法调用警告、非受检参数化可变参数类型警告&#xff08; unchecked parameterized vararg type warning&#xff09;&#xff0c;以及非受检转换警告…

在linux系统上安装Nginx

1、关闭防火墙 systemctl disable firewalld.service 2、上传压缩包并解压到目标文件 cd /usr/local tar -zxvf nginx-1.22.0.tar.gz 3、安装Nginx相关依赖 yum install -y gcc-c zlib zlib-developenssl openssl-devel pcre pcre-devel 4、安装完毕后&#xff0c;进入ng…

Spring Boot集成Mybatis-Plus

Spring Boot集成Mybatis-Plus 1. pom.xml导包 <!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mysql驱动--><dependency><groupId>mysql<…

FastAPI 构建 API 高性能的 web 框架(一)

如果要部署一些大模型一般langchainfastapi&#xff0c;或者fastchat&#xff0c; 先大概了解一下fastapi,本篇主要就是贴几个实际例子。 官方文档地址&#xff1a; https://fastapi.tiangolo.com/zh/ 1 案例1:复旦MOSS大模型fastapi接口服务 来源&#xff1a;大语言模型工程…

大厂容器云实践之路(二)

3-网易蜂巢的DOCKER实践之路 面临问题 场景分析 如何解决 功能性需求&#xff08;基础&#xff09; 第一步 技术支撑公有化 开发流程 场景分析 功能性需求&#xff08;基础&#xff09; 非功能性需求&#xff08;SLA&#xff09; 第二步 产品技术云端化 开发流程 场景分析…

易基因:m5C RNA甲基转移酶及其在癌症中的潜在作用机制|深度综述

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 近年来&#xff0c;5-甲基胞嘧啶&#xff08;m5C&#xff09;RNA修饰已成为通过编码和非编码RNA调控RNA代谢和功能的关键参与者。越来越多的证据表明&#xff0c;m5C可以调控RNA稳定性、…

MOSFET(四):区别JFET

一、JFET及工作原理 N沟道JFET是一种三极结构的半导体器件&#xff0c;包含源极&#xff08;S&#xff09;、漏极&#xff08;D&#xff09;、栅极&#xff08;G&#xff09;工作原理是通过栅源电压控制反型沟道的导电特性。 当栅极-源极电压为零或正电压时&#xff0c;沟道关…

【ChatGLM】大模型之 ChatGLM 部署

目录 1. 资源下载 2. 部署启动 1. 资源下载 HuggingFace 模型权重下载 # install git-lfs git lfs install # download checkpoint # clone the repo git clone https://huggingface.co/THUDM/chatglm-6b 手动模型权重下载 # download checkpoint # clone the repo, ski…

途乐证券|基金重仓股被“撞了一下腰”

中兴通讯昨上演放量长阴走势。 8月7日&#xff0c;A股全天低开低走&#xff0c;创业板领跌。到收盘&#xff0c;沪指跌0.59%&#xff0c;创业板指跌1%。值得一提的是&#xff0c;当天有多只获得基金要点持仓的白马龙头股大跌&#xff0c;其间&#xff0c;在本年二季度颇受基金追…