本文于
2023年3月24日
所写,并根据第十四届蓝桥杯大赛电子类选手资源数据包
所写,使用原理图为SCH_硬件原理图V30.pdf,请注意版本时间与使用原理图。
或非门,于原理图中为SN74HC02DRG4(U25)。
在WR(P42) = 0
或接GND时,直接作为非门使用,因片选芯片**74HC138(U24)**选择的端口为低,未选择为高,通过此芯片后取反可以更容易理解其使用。
P24
或GND(通过J13选择)。当J13接(P42)
时,WR(P42) = 1
时所有输出端口为低,可用于功能锁定,应用较少,暂不介绍。因其具体功能与**74HC138(U24)**直接相关,相关控制可直接观看后文。
非门,此芯片用于超声发射装置,在J2选择1-3、2-4有效。由P10
控制,其主要目的为控制超声波模块实现发声。
P10
实现控制。单片机控制超声波模块发声(此代码由官方提供)。
#include "STC15F2K60S2.H"
#define TX P10 // 发射引脚
#define RX P11 // 接收引脚
void Timer0Init(void) // 12us@12MHz
{
AUXR &= 0x7F; // 定时器时钟12T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0xF4; // 设置定时初值
TH0 = 0xFF; // 设置定时初值
TF0 = 0; // 清除TF0标志
TR0 = 0; // 定时器0停止
}
unsigned char Wave_Recv(void)
{
unsigned char ucDist, ucNum = 10;
TX = 0;
TL0 = 0xF4; // 设置定时初值
TH0 = 0xFF; // 设置定时初值
TR0 = 1; // 定时器0计时
// TX引脚发送40KHz方波信号驱动超声波发送探头
while(ucNum--)
{
while(!TF0);
TX ^= 1;
TF0 = 0;
}
TR0 = 0;
TL0 = 0; // 设置定时初值
TH0 = 0; // 设置定时初值
TR0 = 1;
while(RX && !TF0); // 等待收到脉冲
TR0 = 0;
if(TF0) // 发生溢出
{
TF0 = 0;
ucDist = 255;
}
else // 计算距离
ucDist = ((TH0<<8)+TL0)*0.017;
return ucDist;
}
锁存器,于原理图中名称为74HC573,原理图中有多个此芯片,具体情况如下
当锁存器LE引脚为低电平时,输入端D0~D7输入数据无效,输出端Q0~Q7保持原状态;当锁存器LE引脚为高电平时,输入端D0~D7输入数据无效,输出端Q0~Q7与输入端保持一致。
P0
端口,LE引脚接入 SN74HC02DRG4(U25) 相关端口,具体接线于一、74HC_HCT02 > 2、芯片接入接出 > 接出中对比查看。通过控制P0
端口可控制相关锁存器的输入端口数据,相关控制选择[Control()函数]请看后文。
#include
#include
#include
/*0~F*/
unsigned char code NNum[16] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E, 0xC2, 0x89};
/*数码管输出,传参完全同C语言下printf函数*/
void Nixie_printf(unsigned char *str, ...){
unsigned char Show[17], i, j, flag, Sflag;
va_list ap;
va_start(ap, str);
vsprintf(Show, str, ap);
va_end(ap);
for(i = j = 0; i < 17 && Show[i] != '\0'; i++, j++){
P0 = 0xFF;
Control(6); // Control相关代码:#define Control(NUM) {P2=(P2&0x7F)|(NUM<<5);}
P0 = 0x01 << j;
Control(7);
P0 = 0xFF;
flag = Show[i + 1] == '.' ? 0x80 : 0x00;
if(Show[i] >= '0' && Show[i] <= '9'){
P0 = NNum[Show[i] - '0'] - flag;
} else if (Show[i] >= 'A' && Show[i] <= 'F'){
P0 = NNum[Show[i] - 'A' + 10] - flag;
} else if (Show[i] >= 'a' && Show[i] <= 'f'){
P0 = NNum[Show[i] - 'a' + 10] - flag;
} else if (Show[i] == '-') { // 可加入多组else if
P0 = 0xCF - flag;
} else if (Show[i] == '.'){
if(flag){
if(Sflag) P0 = 0x7F;
else Sflag = 0;
} else {
j--;
Sflag = 1;
}
}
Delay1ms();
}
Control(0); // 全不选
return;
}
#include
/*LED相关bit位设置
传参:
LEDx:LED灯号(1~8)
WriteBit:写入数据(0、1)
注意:写入0LED亮,写入1LED灭
*/
void LED_WriteBit(unsigned char LEDx, unsigned char WriteBit){
Control(4); // Control相关代码:#define Control(NUM) {P2=(P2&0x7F)|(NUM<<5);}
if(WriteBit) P0 |= WriteBit << (LEDx - 1);
else P0 &= ~(WriteBit << (LEDx - 1));
Control(0);
}
/*LED多位写入
传参:
WriteCMD: 写入模式(0~3)
WriteData: 写入数据
注意:写入0LED亮,写入1LED灭
*/
void LED_Writes(unsigned char WriteCMD, unsigned char WriteData){
Control(5); // Control相关代码:#define Control(NUM) {P2=(P2&0x7F)|(NUM<<5);}
switch(WriteCMD){
case 0: // 相关输入位置0。
// 根据WriteData所给出8Bit位数据进行置0,8Bit位中为1的相对应位的数据置0.其他不变
P0 &= ~WriteData;
break;
case 1: // 相关输入位置1,筛选方式同上
P0 |= WriteData;
break;
case 2: // P0与WriteData相等
P0 = WriteData;
break;
}
Control(0);
}
#include
unsigned char code Step_Data[8] = {0xF9, 0xF1, 0xF3, 0xF2, 0xF6, 0xF4, 0xFC, 0xF8};
// 步进电机
/* 步进电机控制(以28BYJ-48为例)
传参:
Start:起始数据(0~7),
Num:步进数(),
Orientation:旋转方向(0、1),
NOrien:默认方向(0、1)
*/
void StepMotor(unsigned char Start, unsigned int Num, bit Orientation, bit NOrien){
Control(5); // Control相关代码:#define Control(NUM) {P2=(P2&0x7F)|(NUM<<5);}
if(NOrien && Start) Start = 8 - Start;
if(Orientation){
while(Num-- != 0){
Start = (Start == 0) ? 7 : Start - 1;
P0 &= Step_Data[Start];
Delay1ms();
}
} else {
while(Num-- != 0){
Start = (Start >= 7) ? 0 : Start + 1;
P0 &= Step_Data[Start];
Delay1ms();
}
}
Control(0);
return;
}
sbit P04 = P0^4;
sbit P05 = P0^5;
sbit P06 = P0^6;
sbit P07 = P0^7;
/*控制其他
传参:
ControlData:待控制物(0~3)
WriteData:写入数据(0、1)
注意:写入0启动,写入1关闭
*/
void ControlOther(unsigned char ControlData, unsigned char WriteData){
Control(5); // Control相关代码:#define Control(NUM) {P2=(P2&0x7F)|(NUM<<5);}
switch(ControlData){
case 0: // 控制继电器
P04 = WriteData;
break;
case 1: // 控制直流电机
P05 = WriteData;
break
case 2: // 控制蜂鸣器
P06 = WriteData;
break;
case 3: // 对外输出音频
P07 = WriteData;
break;
}
Control(0);
}
三八译码器,在原理图中名为74HC138(U24),通过本芯片可以选择开发板中的诸多外设,功能也比较简单,通过P25~P27
可以选择74HC138(U24)相应端口Y0~Y7中一个,及将P25~P27
所组成的8进制数转化为具体的端口数,但需要注意的是被选的端口为低电平,未选端口为高电平。
P25~P27
引脚接74HC138(U24) 中 A0~A3,需要注意,在转换中,P27
为高位。单片机可以同控制P25~P27
实现相关控制功能。
#include
#define Control(NUM) {P2 = (P2 & 0x7F) | (NUM << 5);}
#include
sbit P25 = P2^5;
sbit P26 = P2^6;
sbit P27 = P2^7;
#define Control_None() {P25 = P26 = P27 = 0;} // 无控制
#define Control_LED() {P25 = P26 = 0; P27 = 1;} // 控制LED Y4
#define Control_Other() {P26 = 0; P25 = P27 = 1;} // 控制其他类别(继电器等) Y5
#define Control_Po() {P25 = 0; P26 = P27 = 1;} // 控制数码管位选 Y6
#define Control_Pa() {P25 = P26 = P27 = 1;} // 控制数码管段选 Y7
Eeprom
,在原理图中命名为AT24C02(U4),可存储2KB数据的带电可擦可编程只读存储器。通过IIC控制。IIC中SCL端口接P20
,SDA端口接P21
。在控制前,需要发送其地址,其地址位由A0~A2决定,在原理图中地址位全部接地,故A0~A2全为0,具体通讯方式如下:
上图出自 AT24C02.pdf > P11 > Figure 8. Byte Write。
上图出自 AT24C02.pdf > P12 > Figure 10. Current Address Read。
本IIC通讯控制功能由官方资料提供,相关IIC通讯直接由蓝桥杯提供,这里就不写了。
#include "iic.h"
/**
* @brief 从AT24C02(add)中读出数据da
*
* @param[in] add - AT24C02存储地址
* @param[out] da - 从AT24C02相应地址中读取到的数据
* @return - da
*/
void EEPROM_Read(unsigned char* pucBuf, unsigned char addr, unsigned char num)
{
IIC_Start();
IIC_SendByte(0xA0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xA1);
IIC_WaitAck();
while(num--)
{
*pucBuf++ = IIC_RecByte();
if(num) IIC_SendAck(0);
else IIC_SendAck(1);
}
IIC_Stop();
}
/**
* @brief 向AT24C02(add)中写入数据val
*
* @param[in] add - AT24C02存储地址
* @param[in] val - 待写入AT24C02相应地址的数据
* @return - none
*/
void EEPROM_Write(unsigned char* pucBuf, unsigned char addr, unsigned char num)
{
IIC_Start();
IIC_SendByte(0xA0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while(num--)
{
IIC_SendByte(*pucBuf++);
IIC_WaitAck();
IIC_Delay(200);
}
IIC_Stop();
}
数字温度传感器,在原理图中为DS18B20(U5),采用单总线模式,所幸单总线交互的源代码在比赛时由蓝桥杯官方提供,**DS18B20(U5)**会返回两个字节数据,先返回低字节数据,再返回高字节数据,返回字节数据如下表。
BIT 7 | BIT 6 | BIT 5 | BIT 4 | BIT 3 | BIT 2 | BIT 1 | BIT 0 | |
---|---|---|---|---|---|---|---|---|
LS BYTE | 23 | 22 | 21 | 20 | 2-1 | 2-2 | 2-3 | 2-4 |
BIT 15 | BIT 14 | BIT 13 | BIT 12 | BIT 11 | BIT 10 | BIT 9 | BIT 8 | |
---|---|---|---|---|---|---|---|---|
MS BYTE | S | S | S | S | S | 26 | 25 | 24 |
S = SIGN
此表出自 DS18B20.pdf -> P4 -> Figure 2. Temperature Register Format。
备注:LS BYTE为低字节,MS BYTE为高字节。
在计算中可以采用(((MS_BYTE & 0x0F) << 8) + LS_BYTE) / 16.0
的方式计算。
因蓝桥杯官方已给出相关通讯方式,下面直接介绍蓝桥杯官方所给的交互方法。
更多功能于 DS18B20.pdf > P10~12 > ROM COMMANDS、DS18B20 FUNCTION COMMANDS。
代码为官方提供,相关库函数由官方提供onewire库提供。
#include "onewire.h"
// 温度数据转换(可以将rd_temperature转换为实际温度,但无法判断温度正负)
#define Conversion_Temperature(DATA) ((DATA & 0x07FF) / 16.0)
// 温度数据获取代码
unsigned int rd_temperature(void)
{
unsigned char low, high;
init_ds18b20(); // 初始化
Write_DS18B20(0xCC); // 跳过ROM
Write_DS18B20(0x44); // 转换温度
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE); // 读暂存器
low = Read_DS18B20(); // 低字节
high = Read_DS18B20(); // 高字节
return (high<<8)+low;
}
时钟芯片,在原理图中为 DS1302M/TR(U19) 可以用于计时,计时时间包括年月日分秒周,因蓝桥杯官方已给出相关通讯代码,直接介绍其初始化方式与读取方式。
将16进制转换为BCD码的方式:
((DATA / 10) << 4) + DATA % 10
BCD码转16进制数据:
(DATA >> 4) * 10 + (DATA & 0x0F)
更多功能可查看 DS1302.pdf > P9 > Table 3. Register Address/Definition。
源代码由蓝桥杯官方提供。
#include "ds1302.h"
// 设置时钟,pucRtc所指向数组最小为3字节
void Set_RTC(unsigned char* pucRtc)
{
unsigned char temp;
Write_Ds1302_Byte(0x8E, 0); // WP=0:允许写操作
temp = ((pucRtc[0]/10)<<4)+pucRtc[0]%10;
Write_Ds1302_Byte(0x84, temp); // 设置时
temp = ((pucRtc[1]/10)<<4)+pucRtc[1]%10;
Write_Ds1302_Byte(0x82, temp); // 设置分
temp = ((pucRtc[2]/10)<<4)+pucRtc[2]%10;
Write_Ds1302_Byte(0x80, temp); // 设置秒
Write_Ds1302_Byte(0x8E, 0x80); // WP=1:禁止写操作
}
// 读取时钟,pucRtc所指向数组最小为3字节
void Read_RTC(unsigned char* pucRtc)
{
unsigned char temp;
temp = Read_Ds1302_Byte(0x85); // 读取时
pucRtc[0] = (temp>>4)*10+(temp&0x0F);
temp = Read_Ds1302_Byte(0x83); // 读取分
pucRtc[1] = (temp>>4)*10+(temp&0x0F);
temp = Read_Ds1302_Byte(0x81); // 读取秒
pucRtc[2] = (temp>>4)*10+(temp&0x0F);
}
集成运放,原理图中为U26,不做介绍,其输出AIN2于芯片PCF8951(U16) 中进行介绍。
音频放大电路,在原理图中为LM386D(U14),近似为功放,不做介绍。
NE555,在原理图中为 NE555(U15) 用于产生不同频率的波形,不做介绍。
低功耗8位CMOS数据采集设备,在原理图中为PCF8591(U16),其通讯方式同样为IIC通讯,其有四个模拟输入端口与一个模拟输出端口,其功能都可根据设置自行修改,在发送数据时,需要先发送 PCF8591(U16) 的地址值,在原理图中其A0~A2均接地,所以其地址为1001 000X B
(X为写入或读取字节)。
上图出自 PCF8951.pdf > P1 > Fig.4 Address byte.
以下为 PCF8591(U16) 相关功能的使用。
ADC数据转换
(flaot)(Vin / 51.2)
DAC数据转换:
(unsigned char)(Vout * 51.2)
更多设置可参考 PCF8951.pdf > P6 > Fig.5 Control byte。
DAC功能可参考 PCF8951.pdf > P8。
ADC功能可参考 PCF8951.pdf > P9~P10。
代码由蓝桥杯官方给出。
#include "iic.h"
// DAC输出值转换
#define PCF8951_VoutConversion(Vout) (unsigned char)(Vout * 51.2)
// ADC输入值转换
#define PCF8951_VinConversion(Vin) (float)(Vin / 51.2)
// 通过I2C总线读取ADC结果
unsigned char PCF8591_Adc(void)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x43); // 允许DAC,ADC通道3
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
return temp;
}
// DAC功能
void PCF8591_Dac(unsigned char dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x43); // 允许DAC,ADC通道3
IIC_WaitAck();
IIC_SendByte(dat); // dat-输出数模转换的数据
IIC_WaitAck();
IIC_Stop();
}
高耐压、大电流复合晶体管阵列,在原理图中为 ULN2003(U10),因STC芯片无法驱动大电流,故使用此芯片,需要注意:此芯片相当于一个非门。
接入
参考三、74HC_HCT573 > 2、引脚接入接出 > 接出。
接出
参考三、74HC_HCT573 > 3、单片机控制 > 其他控制。
USB转接芯片,不多做介绍。