十六届蓝桥杯单片机组模拟Ⅱ程序(基于B站西风大模板)
·
1.按键功能部分
/*按键功能区*/
switch(Key_down)
{
case 4: //模式切换按键
if(Dis_mode != 3) //保证在触发状态下无效
if(++Dis_mode == 3) Dis_mode = 0; //模式循环切换
Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位
Dis_mode2 = 0;//历史子界面复位
break;
case 5: //历史子界面切换按键
if(Dis_mode == 2) //保证仅在触发模式下生效
if(++Dis_mode2 == 3) Dis_mode2 = 0;
break;
case 8: //清空历史按键(历史界面有效)
if(Dis_mode == 2)
{
Real_Save_flag = 0;//索引清空(实质上没有清空存储数组,但是显示效果和清空一样)
}
break;
}
2.时间界面显示部分
case 0: //时间显示模式
Seg_dis[0] = Time_ds1302[0] /16 %16;
Seg_dis[1] = Time_ds1302[0] %16;
Seg_dis[3] = Time_ds1302[1] /16 %16;
Seg_dis[4] = Time_ds1302[1] %16;
Seg_dis[6] = Time_ds1302[2] /16 %16;
Seg_dis[7] = Time_ds1302[2] %16;
Seg_dis[2] = 11;Seg_dis[5] = 11; //显示时间,11-“-”
break;
3.电压数据显示部分
/*数据采集区域*/
Read_time(Time_ds1302);//读RTC
/*读AD采样值(0~255)*/
Right_V_data = AD_Read(0x43);//光敏电阻
R_V_data = AD_Read(0x41);//电位器
case 1: //数据显示模式
Seg_dis[0] = 12;Seg_dis[4] = 13;//0-P,4-U
Seg_dian[1] = 1;Seg_dian[5] = 1;//小数点
//将0~255乘100再除51,其转为0~5且扩大100倍,避免小数
//注意要进行数据类型强转(500 > 255,用int)
Seg_dis[1] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /100 %10);
Seg_dis[2] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /10 %10);
Seg_dis[3] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /1 %10);//光敏电压
Seg_dis[5] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /100 %10);
Seg_dis[6] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /10 %10);
Seg_dis[7] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /1 %10);//电位器电压
break;
4.历史查询界面及显示处理
void find_Save() //判断是否有记录时间。有-读取显示,无-显示“-”
{
if(Dis_mode2 < Real_Save_flag)
{
Seg_dis[2] = Save_time[Dis_mode2][0] /16 %16;//显示触发时间(用Dis_mode2作为索引)
Seg_dis[3] = Save_time[Dis_mode2][0] %16;
Seg_dis[4] = Save_time[Dis_mode2][1] /16 %16;
Seg_dis[5] = Save_time[Dis_mode2][1] %16;
Seg_dis[6] = Save_time[Dis_mode2][2] /16 %16;
Seg_dis[7] = Save_time[Dis_mode2][2] %16;
}
else
{
Seg_dis[2] = 11;Seg_dis[3] = 11;//“-”
Seg_dis[4] = 11;Seg_dis[5] = 11;
Seg_dis[6] = 11;Seg_dis[7] = 11;
}
}
case 2: //历史显示模式
Seg_dis[0] = 14;//A
switch(Dis_mode2) //子界面判断
{
case 0:
Seg_dis[1] = 1;
find_Save();
break;
case 1:
Seg_dis[1] = 2;
find_Save();
break;
case 2:
Seg_dis[1] = 3;
find_Save();
break;
}
break;
5.触发界面显示与触发逻辑、数据记录逻辑
/*触发逻辑及其处理操作*/
if(Dis_mode != 3) //保证非触发时可进行触发判断
{
if(Right_V_data < R_V_data) //触发条件判断:光敏 < 电位器
{
Old_dis_mode = Dis_mode; //保存当前模式
Dis_mode = 3; //切换为触发模式
Save_time[2][0] = Save_time[1][0];
Save_time[2][1] = Save_time[1][1];
Save_time[2][2] = Save_time[1][2];
Save_time[1][0] = Save_time[0][0];
Save_time[1][1] = Save_time[0][1];
Save_time[1][2] = Save_time[0][2];
Save_time[0][0] = Time_ds1302[0];
Save_time[0][1] = Time_ds1302[1];
Save_time[0][2] = Time_ds1302[2];//时间记录数组传值(3-舍弃,2->3,1->2,Time_ds1302 -> 1)
Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位(若不复位,数据模式时触发,小数点会一直保留下来)
if(++Real_Save_flag == 3) Real_Save_flag = 3;//每次记录,历史最高记录次数加1,直到记满
}
}
case 3: //触发显示模式
Seg_dis[0] = 15;Seg_dis[1] = 15;//C
Seg_dis[2] = Save_time[0][0] /16 %16;
Seg_dis[3] = Save_time[0][0] %16;
Seg_dis[4] = Save_time[0][1] /16 %16;
Seg_dis[5] = Save_time[0][1] %16;
Seg_dis[6] = Save_time[0][2] /16 %16;
Seg_dis[7] = Save_time[0][2] %16;
break;
if(Dis_mode == 3) //触发模式持续时间计时(触发模式下有效)
{
if(++Time_dis_mode3 == 3000)//3s计时
{
Time_dis_mode3 = 0;
Dis_mode = Old_dis_mode; //回到原界面
}
}
6.LED显示
/*其他显示(LED、蜂鸣器、继电器)*/
void Other_Dis_Take()
{
LED_dis[0] = 1 * (Dis_mode == 0);//时间界面LED1亮
LED_dis[1] = 1 * (Dis_mode == 1);//数据界面LED2亮
LED_dis[2] = 1 * (Dis_mode == 2);//历史界面LED3亮
LED_dis[7] = 1 * (Dis_mode == 3);//触发界面LED8亮
}
完整程序如下:
main.c
/*头文件*/
#include <STC15F2K60S2.H>
#include <intrins.H>
#include <stdio.H>
#include <string.H>
#include <Init.H>
#include <LED.H>
#include <Key.H>
#include <Seg.H>
#include <DS1302.H>
#include <I2C.H>
/*全局变量声明区域*/
unsigned char Key_show_down;//按键减速变量
unsigned char Key_val,Key_old,Key_up,Key_down;//按键扫描变量,键值、前值、上升沿、下降沿
unsigned int Seg_show_down;//数码管减速变量
unsigned char Seg_pos;//数码管扫描位变量
unsigned char Seg_dis[] = {10,10,10,10,10,10,10,10};//数码管显示缓存数组
unsigned char Seg_dian[] = {0,0,0,0,0,0,0,0};//小数点显示缓存数组
unsigned char LED_dis[] = {0,0,0,0,0,0,0,0};//LED显示缓存数组
/*程序逻辑变量*/
unsigned char Dis_mode,Dis_mode2,Old_dis_mode;//模式选择,0-时间,1-数据,2-历史,3-触发。历史模式子界面。触发前模式
unsigned char Time_ds1302[] = {0x23,0x59,0x00};//RTC
unsigned char Save_time[3][3] = {{0x11,0x00,0x00},{0x22,0x00,0x00},{0x33,0x00,0x00}};//触发时间记录
unsigned char Right_V_data,R_V_data;//光敏原始值,电位器原始值
unsigned char Real_Save_flag;//历史记录次数标志,0~3,0-未记录。
unsigned int Time_dis_mode3;//触发模式持续时长
/*函数定义区域*/
/*按键功能函数*/
void Key_Take()
{
if(Key_show_down) return;
Key_show_down = 1;//按键减速
Key_val = Key_jz();//读取按键键值(矩阵)
Key_down = Key_val &(Key_old ^ Key_val);//读取按键下降沿
Key_up = ~Key_val &(Key_old ^ Key_val);//读取按键上升沿
Key_old = Key_val;//辅助扫描
/*按键功能区*/
switch(Key_down)
{
case 4: //模式切换按键
if(Dis_mode != 3) //保证在触发状态下无效
if(++Dis_mode == 3) Dis_mode = 0; //模式循环切换
Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位
Dis_mode2 = 0;//历史子界面复位
break;
case 5: //历史子界面切换按键
if(Dis_mode == 2) //保证仅在触发模式下生效
if(++Dis_mode2 == 3) Dis_mode2 = 0;
break;
case 8: //清空历史按键(历史界面有效)
if(Dis_mode == 2)
{
Real_Save_flag = 0;//索引清空(实质上没有清空存储数组,但是显示效果和清空一样)
}
break;
}
}
void find_Save() //判断是否有记录时间。有-读取显示,无-显示“-”
{
if(Dis_mode2 < Real_Save_flag)
{
Seg_dis[2] = Save_time[Dis_mode2][0] /16 %16;//显示触发时间(用Dis_mode2作为索引)
Seg_dis[3] = Save_time[Dis_mode2][0] %16;
Seg_dis[4] = Save_time[Dis_mode2][1] /16 %16;
Seg_dis[5] = Save_time[Dis_mode2][1] %16;
Seg_dis[6] = Save_time[Dis_mode2][2] /16 %16;
Seg_dis[7] = Save_time[Dis_mode2][2] %16;
}
else
{
Seg_dis[2] = 11;Seg_dis[3] = 11;//“-”
Seg_dis[4] = 11;Seg_dis[5] = 11;
Seg_dis[6] = 11;Seg_dis[7] = 11;
}
}
/*数据显示功能函数*/
void Seg_Take()
{
if(Seg_show_down) return;
Seg_show_down = 1;//数码管减速
/*数据采集区域*/
Read_time(Time_ds1302);//读RTC
Right_V_data = AD_Read(0x43);
R_V_data = AD_Read(0x41);//读AD采样值(0~255)
/*触发逻辑及其处理操作*/
if(Dis_mode != 3) //保证非触发时可进行触发判断
{
if(Right_V_data < R_V_data) //触发条件判断:光敏 < 电位器
{
Old_dis_mode = Dis_mode; //保存当前模式
Dis_mode = 3; //切换为触发模式
Save_time[2][0] = Save_time[1][0];
Save_time[2][1] = Save_time[1][1];
Save_time[2][2] = Save_time[1][2];
Save_time[1][0] = Save_time[0][0];
Save_time[1][1] = Save_time[0][1];
Save_time[1][2] = Save_time[0][2];
Save_time[0][0] = Time_ds1302[0];
Save_time[0][1] = Time_ds1302[1];
Save_time[0][2] = Time_ds1302[2];//时间记录数组传值(3-舍弃,2->3,1->2,Time_ds1302 -> 1)
Seg_dian[1] = 0;Seg_dian[5] = 0;//小数点复位(若不复位,数据模式时触发,小数点会一直保留下来)
if(++Real_Save_flag == 3) Real_Save_flag = 3;//每次记录,历史最高记录次数加1,直到记满
}
}
/*数据显示区域*/
switch(Dis_mode)
{
case 0: //时间显示模式
Seg_dis[0] = Time_ds1302[0] /16 %16;
Seg_dis[1] = Time_ds1302[0] %16;
Seg_dis[3] = Time_ds1302[1] /16 %16;
Seg_dis[4] = Time_ds1302[1] %16;
Seg_dis[6] = Time_ds1302[2] /16 %16;
Seg_dis[7] = Time_ds1302[2] %16;
Seg_dis[2] = 11;Seg_dis[5] = 11; //显示时间,11-“-”
break;
case 1: //数据显示模式
Seg_dis[0] = 12;Seg_dis[4] = 13;//0-P,4-U
Seg_dian[1] = 1;Seg_dian[5] = 1;//小数点
Seg_dis[1] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /100 %10);//将0~255乘100再除51,其转为0~5且扩大100倍,避免小数
Seg_dis[2] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /10 %10);//注意要进行数据类型强转(500 > 255,用int)
Seg_dis[3] = (unsigned char)(((unsigned int)Right_V_data * 100 /51) /1 %10);//光敏电压
Seg_dis[5] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /100 %10);
Seg_dis[6] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /10 %10);
Seg_dis[7] = (unsigned char)(((unsigned int)R_V_data * 100 /51) /1 %10);//电位器电压
break;
case 2: //历史显示模式
Seg_dis[0] = 14;//A
switch(Dis_mode2) //子界面判断
{
case 0:
Seg_dis[1] = 1;
find_Save();
break;
case 1:
Seg_dis[1] = 2;
find_Save();
break;
case 2:
Seg_dis[1] = 3;
find_Save();
break;
}
break;
case 3: //触发显示模式
Seg_dis[0] = 15;Seg_dis[1] = 15;//C
Seg_dis[2] = Save_time[0][0] /16 %16;
Seg_dis[3] = Save_time[0][0] %16;
Seg_dis[4] = Save_time[0][1] /16 %16;
Seg_dis[5] = Save_time[0][1] %16;
Seg_dis[6] = Save_time[0][2] /16 %16;
Seg_dis[7] = Save_time[0][2] %16;
break;
}
}
/*其他显示(LED、蜂鸣器、继电器)*/
void Other_Dis_Take()
{
LED_dis[0] = 1 * (Dis_mode == 0);//时间界面LED1亮
LED_dis[1] = 1 * (Dis_mode == 1);//数据界面LED2亮
LED_dis[2] = 1 * (Dis_mode == 2);//历史界面LED3亮
LED_dis[7] = 1 * (Dis_mode == 3);//触发界面LED8亮
}
/*定时器1初始化(系统定时器)*/
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
EA = 1;
}
/*定时器1中断服务函数*/
void Timer1_Take() interrupt 3
{
if(++Key_show_down == 10) Key_show_down = 0;//按键减速10ms
if(++Seg_show_down == 200) Seg_show_down = 0;//数码管减速200ms
if(++Seg_pos == 8) Seg_pos = 0;//数码管位扫描
Display(Seg_pos,Seg_dis[Seg_pos],Seg_dian[Seg_pos]);//数码管显示函数
LED(Seg_pos,LED_dis[Seg_pos]);//LED显示函数
if(Dis_mode == 3) //触发模式持续时间计时(触发模式下有效)
{
if(++Time_dis_mode3 == 3000)//3s计时
{
Time_dis_mode3 = 0;
Dis_mode = Old_dis_mode; //回到原界面
}
}
}
/*主程序*/
void main()
{
Init();//系统初始化(关闭蜂鸣器、LED)
Timer1Init();//定时器初始化
Write_time(Time_ds1302);//初始时间写入
while(1)
{
Key_Take();
Seg_Take();
Other_Dis_Take();
}
}
Init.c
#include <STC15F2K60S2.H>
void Init()
{
P0 = 0xff;//LED全灭
P2 = P2 & 0x1f | 0x80;//打开LED通道
P2 &= 0x1f;//关闭通道
P0 = 0x00;//关闭蜂鸣器继电器(经过ULN2003芯片自带反相功能)
P2 = P2 & 0x1f | 0xA0;//打开LED通道
P2 &= 0x1f;//关闭通道
}
LED.c
#include <STC15F2K60S2.H>
/*
LED控制
参数:控制位,是否使能(点亮)
*/
void LED(unsigned char wei,unsigned char enable)
{
static unsigned char temp = 0x00;
static unsigned char temp_old = 0xff;
if(enable)
temp |= 0x01 << wei;//给点亮位传1
else
temp &= ~(0x01<<wei);//给点亮位传0
if(temp != temp_old) //避免译码器、锁存器频繁开关,仅在LED状态变化时打开通道
{
P0 = ~temp;//将点亮位传给P2(给0亮)
P2 = P2 & 0x1f | 0x80;//打开LED通道
P2 &= 0x1f;//关闭通道
temp_old = temp;//新旧值交换
}
}
Key.c
#include <STC15F2K60S2.H>
unsigned char Key_jz()
{
unsigned char key = 0;
/*
使用串口时,用P30,P31,有硬件冲突。
为防止对串口的影响,关闭轮询定时器(定时器1),结束按键扫描后再打开定时器。
*/
ET1 = 0;
P3=0xff;P42=1;P44=0;//一列导通,扫描行号(0为通)
if(P30==0) key=7;
if(P31==0) key=6;
if(P32==0) key=5;
if(P33==0) key=4;
P3=0xff;P42=0;P44=1;
if(P30==0) key=11;
if(P31==0) key=10;
if(P32==0) key=9;
if(P33==0) key=8;
P3=0xff;P42=1;P44=1;P35=0;
if(P30==0) key=15;
if(P31==0) key=14;
if(P32==0) key=13;
if(P33==0) key=12;
// P3=0xff;P42=1;P44=1;P34=0;
// if(P30==0) key=19;
// if(P31==0) key=18;
// if(P32==0) key=17;
// if(P33==0) key=16;
P3=0xff;ET1 = 1;//重新开启轮询定时器(为何令P3=0xff待验证)·············
return key;//返回键值
}
unsigned char Key_dl()
{
unsigned char key = 0;
if(P30 == 0) key = 7;
if(P31 == 0) key = 6;
if(P32 == 0) key = 5;
if(P33 == 0) key = 4;
return key;
}
Seg.c
#include <STC15F2K60S2.H>
code unsigned char DX[] = {0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8,0x80,0x90,0xff,
0xBF,0x8C,0xC1,0x88,0xC6
};//0~9,10-熄灭,11-“-”,12-P,13-U,14-A,15-C
void Display(unsigned char wei,unsigned char duan,unsigned char dian)
{
P0 = 0xff;//段选消影
P2 = P2 & 0x1f | 0xe0;//打开段选通道
P2 &= 0x1f;//关闭通道
P0 = 0x01 << wei;//不使用数组方式,直接使用位操作选择点亮位(类比LED)
P2 = P2 & 0x1f | 0xc0;//打开位选通道
P2 &= 0x1f;//关闭通道
P0 = DX[duan];
if(dian)
P0 &= 0x7f;//有小数点,将其按位与0x7F,将最高位0传递到P0
P2 = P2 & 0x1f | 0xe0;//打开段选通道
P2 &= 0x1f;//关闭通道
}
DS1302.c (这里我添加了DS1302的12小时模式的显示,经测试是正常的)
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <STC15F2K60S2.H>
#include <intrins.H>
sbit SCK = P1^7;
sbit RST = P1^3;
sbit SDA = P2^3;
//
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
/*
写时间(24h)
参数:时、分、秒(数组)
*/
void Write_time(unsigned char time_ds1302[3])
{
Write_Ds1302_Byte(0x8e,0x00);//去除写保护
Write_Ds1302_Byte(0x84,time_ds1302[0]);//时
Write_Ds1302_Byte(0x82,time_ds1302[1]);//分
Write_Ds1302_Byte(0x80,time_ds1302[2]);//写入时间
Write_Ds1302_Byte(0x8e,0x80);//打开写保护
}
/*
读时间(24h)
参数:时、分、秒(数组)
*/
void Read_time(unsigned char time_ds1302[3])
{
time_ds1302[0] = Read_Ds1302_Byte(0x85);
time_ds1302[1] = Read_Ds1302_Byte(0x83);
time_ds1302[2] = Read_Ds1302_Byte(0x81);
}
/*
写时间(12h)
参数:时、分、秒(数组),AT:早晚,0早1晚
*/
void Write_time_12(unsigned char AT,unsigned char time_ds1302[3])
{
Write_Ds1302_Byte(0x8e,0x00);//打开写保护
if(AT)
Write_Ds1302_Byte(0x84,time_ds1302[0] | 0xA0);//下午,bit5为1,12小时制,bit7为1
else
Write_Ds1302_Byte(0x84,time_ds1302[0] | 0x80);//上午,bit5为0,12小时制,bit7为1
Write_Ds1302_Byte(0x82,time_ds1302[1]);
Write_Ds1302_Byte(0x80,time_ds1302[2]);
Write_Ds1302_Byte(0x8e,0x80);//关闭写保护
}
/*
读时间(12h)
参数:时、分、秒(数组)
返回值:AT:早晚,2早1晚
*/
unsigned char Read_time_12(unsigned char time_ds1302[3])
{
unsigned char AT = 0;
if((Read_Ds1302_Byte(0x85) & 0x20) == 0x20)
AT = 1;
else
AT = 2;
time_ds1302[0] = Read_Ds1302_Byte(0x85) & 0x1f;
time_ds1302[1] = Read_Ds1302_Byte(0x83);
time_ds1302[2] = Read_Ds1302_Byte(0x81);
return AT;
}
IIC.c (在使用AD进行数据读取时,若读取到的数据闪烁的非常快,可以将这里的DELAY_TIME的值由10改为5)(官方提供的文件里DELAY_TIME的值为10,这里我已经进行了修改)
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <STC15F2K60S2.H>
#include <intrins.H>
#define DELAY_TIME 5
sbit scl = P2^0;
sbit sda = P2^1;
//I2C等待
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//I2C开始
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//I2C停止
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//I2C发送数据
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//I2C接收数据
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//I2C等待应带
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//I2C发送应答
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
/*
读AD值
参数:通道(光敏0x41,变阻器0x43)
(注意:同时读取两路信号时要用光敏电阻变量读取电位器值,电位器变量读取光敏电阻值)
返回值:AD值(0-255)
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
| PCF8591有一个模数转换值存储区,当主机未发送读取某一通道指令时,该区存储值默认128。 |
|有多路同时读取时,如光敏(AIN1)和电位器(AIN3),读光敏时调用读取函数将通道编码发送给芯片, |
|该芯片返回的是存储区预存的值(128),同时将通道1的值读取出来,之后读取电位器时将1通道的值给 |
|电位器,同时将3通道值读出存储,以此循环 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
unsigned char AD_Read(unsigned char adder)
{
unsigned char temp = 0;
I2CStart();
I2CSendByte(0x90);//发送写命令
I2CWaitAck();//等待应答
I2CSendByte(adder);//发送地址(通道,光敏0x41,变阻器0x43)
I2CWaitAck();//等待应答
I2CStart();
I2CSendByte(0x91);//发送读命令
I2CWaitAck();//等待应答
temp = I2CReceiveByte();//接收数据
I2CSendAck(1);//发送非应答信号
I2CStop();//停止
return temp;
}
/*
写入DA值(向PCF8591)
参数:数字量(0~255对应输出模拟量0~5V)
(注意:使用时可用对应模拟量乘51,从J3的19号引脚读出)
*/
void DA_Write(unsigned char date)
{
I2CStart();
I2CSendByte(0x90);//发送写命令
I2CWaitAck();//等待应答
I2CSendByte(0x40);//使能DA转换(40~43均可,尽量与题目涉及模块保持一致,e.g.涉及光敏-41)
I2CWaitAck();//等待应答
I2CSendByte(date);//使能DA转换
I2CWaitAck();//等待应答
I2CStop();//停止
}
至此,十六届蓝桥杯单片机模拟Ⅱ试题的程序部分已经全部结束,经4T网站测评为满分,但是这里很多程序写的相对冗余,尤其是三次历史记录时间的传值部分。因为是在追求刷题速度,而未对其进行优化,希望看到这篇帖子的朋友有更好的写法能够分享出来共同交流。
更多推荐

所有评论(0)