【蓝桥杯—单片机】第十一届省赛真题代码题解题笔记 | 省赛 | 真题 | 代码题 | 刷题 | 笔记
第十一届省赛真题代码部分
- 前言
- 赛题代码思路笔记
- 竞赛板配置
- 内部振荡器频率设定
- 键盘工作模式跳线
- 扩展方式跳线
- 建立模板
- 明确设计要求和初始状态
- 显示功能部分
- 数据界面
- 第一部分
- 第二部分
- 第三部分
- 调试时发现的问题
- 参数设置界面
- 第一部分
- 第二部分和第四部分
- 第三部分和第五部分
- 按键功能部分
- S4:“界面切换”按键
- 从数据界面切换到参数设置界面
- 从参数设置界面切换到数据界面
- 调试时发现的问题
- S5:“参数切换”按键
- S6:“加”按键 和 S7:"减"按键
- 调试时发现的问题
- LED指示灯功能部分
- L1、L2、L3
- L4
- DAC输出功能部分
- 最终代码
- User文件
- main.c
- Driver文件
- Init.c
- LED.c
- Seg.c
- Key.c
- onewire.c
- iic.c
- 结语
前言
本文是对蓝桥杯第十一届省赛真题的代码题做的解题笔记,只记录了代码的思路,大模板用的是B站西风老师的2024年版大模板,具体内容就不再重复记录了。
思路参考西风第十四讲内容(资料链接已经在下方给出)
https://www.bilibili.com/video/BV1TR4y1k7iz?p=23&vd_source=e2191f89c557f5ac44bb6c7aa3967c7c
关于蓝桥杯第十一届省赛真题可以在官网查看(资料链接已经在下方给出)
https://www.lanqiao.cn/courses/2786/learning/?id=100643&compatibility=false
赛题代码思路笔记
竞赛板配置
根据赛题要求完成竞赛板的配置
内部振荡器频率设定
在stc中更改输入用户程序运行时的IRC频率为12MHz
键盘工作模式跳线
配置为BTN按键模式
扩展方式跳线
配置为IO模式
建立模板
根据赛题中的硬件框图确定本赛题的框架
如图,在Driver文件夹里建立LED、数码管、按键、iic、onewire模块,在User文件夹中建立主函数模块(大模板参考西风老师的2024版大模板)。
明确设计要求和初始状态
显示功能部分
由题可知,数码管显示一共两个界面。
可以引入一个变量Seg_Disp_Mode来标记当前显示界面,因为只有两个界面,所以可以定义为bit型而不是unsigned char型以此来节省空间。
bit Seg_Disp_Mode; //数码管显示模式变量 0-数据界面 1-参数界面
再在信息处理函数的数码管部分中对当前显示模式进行判断。
判断完成后在各部分写入供该界面显示的代码。
void Seg_Proc()
{
/*数码管减速*/
if(Seg_Flag) return;
Seg_Flag = 1;
/*信息获取区域*/
/*数码管显示区域*/
if(Seg_Disp_Mode == 0)
{
}
else
{
}
}
数据界面
下面来完成第一个界面的编写。
由题目可知,数据界面由三部分组成:
- 第一部分为提示符部分,是数码管的第一位,在该界面下全程显示字母C。
- 第二部分为熄灭部分,是数码管的第2到6位,在该界面下全程熄灭。
- 第三部分为实时温度显示界面,是数码管的第7到8位,在该界面下根据实时监测的温度不同显示数字实时变动。
在Driver文件夹下的Seg模块中的显示数组中加入用于表示熄灭的0xff,表示字母,表示字母C的0xc6,表示字母P的0x8c。
//code unsigned char seg_dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xc6,0x8c};//0-9为数字,10为熄灭,11为C,12为P
更改主模块中对应数码管控制列表中的值来控制数码管的显示情况。
第一部分
标识部分对应的数码管为第一位,更改主模块中对应数码管控制列表Seg_Buf中第一位对应的数,使其对应上Seg模块中的显示数组中的字母C。
Seg_Buf[0] = 12;//C
第二部分
熄灭部分,是数码管的第2到6位,全程为熄灭状态,在创建主函数中的Seg_Buf列表时其中的元素已经置为对应Seg模块中的显示数组中的熄灭,所以不要再做额外的更改。
第三部分
数码管显示的后两位为实时的温度数据,可以引入一个变量Temperature来存储当前获取的温度值。又由题可知,显示的温度值不含小数部分,所以可以直接用unsigned char型。
unsigned char Temperature; //实时温度变量
温度获取函数返回值为float型,将其返回值赋给unsigned char型时要先对其经行强制类型转换。
Temperature = (unsigned char)rd_temperature();
直接用整除10和取余10的形式来取出Temperature的个位和十位。
Seg_Buf[6] = Temperature / 10 ;
Seg_Buf[7] = Temperature % 10;
调试时发现的问题
在调试中发现,上电时并不是立刻显示实时温度值,而是会先显示85.00,要去掉85.00的干扰,可以在上电后先读取温度覆盖掉默认的85.00,再调用信息处理函数实现显示。
需要注意的是,单独调用温度读取函数时要再加一个750ms的延迟,因为DS18B20实现温度读取一次是750ms,如果不用延迟的话,无法实现完整的读取。
rd_temperture();
Delay750ms();
参数设置界面
由题目可知,数据界面由五部分组成:
- 第一部分为提示符部分,是数码管的第一位,在该界面下全程显示字母P。
- 第二部分为熄灭部分,是数码管的第2到3位,在该界面下全程熄灭。
- 第三部分为温度上限参数部分,是数码管的第4到5位。
- 第四部分为熄灭部分,是数码管的第6位,在该界面下全程熄灭。
- 第五部分为温度下限参数部分,是数码管的最后两位。
第一部分
标识部分对应的数码管为第一位,更改主模块中对应数码管控制列表Seg_Buf中第一位对应的数,使其对应上Seg模块中的显示数组中的字母P。
Seg_Buf[0] = 13;//C
第二部分和第四部分
不动
第三部分和第五部分
注意:题目涉及到一个可更改大小的参数时一般需要设置两个变量来标识此参数,一个用于实时的大小更改,一个用于实现控制
为什么要用两个变量,而不用一个?
答:如果只用一个变量,那么该参数在更改时同时会对它所要控制的原件产生影响,对实际的功能有干扰。
例如:当前设置值为24,需要设置的值为26,而蜂鸣器触发条件为25,则当前设置值在更改时会触发蜂鸣,但实际外部检测值并没有到要触发它的状态。
设置两个数组变量用于温度参数的显示和控制。数组的前一位为温度上限参数,后一位为温度下限参数。
unsigned char Temp_Disp[2] = {30,20};//温度变量显示数组
unsigned char Temp_Ctorl[2] = {30,20};//温度变量控制数组
对于显示只要更改数码管控制列表Seg_Buf中对应位置的值即可实现。
Seg_Buf[3] = Temp_Disp[0] / 10 % 10;
Seg_Buf[4] = Temp_Disp[0] % 10;
Seg_Buf[6] = Temp_Disp[1] / 10 % 10;
Seg_Buf[7] = Temp_Disp[1] % 10;
按键功能部分
在大模板主模块的按键处理函数中,用Key_Down判断按键按下的值。因为题目中按键涉及到4、5、6、7,所以可以添加一个switch对当前按下的按键进行一个判断。
判断完成后在各部分写入供该按键需要实现的功能的代码即可。
void Key_Proc()
{
if (Key_Flag)return;
Key_Flag = 1; // 设置标志位,防止重复进入
Key_Val = Key_Read(); // 读取按键值
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:
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
S4:“界面切换”按键
该按键按下后实现了界面的切换。
在显示功能部分,我们已经定义了一个变量Seg_Disp_Mode来标记当前显示的界面,当Seg_Disp_Mode为0时是数据界面,为1时是参数设置界面。
因为变量Seg_Disp_Mode是bit型数据,只在0和1之间变换,所以可以直接用异或来运算。
Seg_Disp_Mode ^= 1;
从数据界面切换到参数设置界面
当按键按下后,Seg_Disp_Mode数值变为1时,界面从数据界面切换到参数设置界面。
if(Seg_Disp_Mode == 1)//从数据界面切换到参数设置界面
{
}
在该界面中,刚完成切换时显示的是当前用于控制相关原件的温度参数的值,即Temp_Ctrol的值。
但是Temp_Ctrol在该界面完成温度参数的设置前一直保持不变,所以不能对Temp_Ctrol经行更改。而要对显示变量Temp_Disp进行更改,且使得刚切换时候显示的是控制值,所以要将控制值赋给显示值。
Temp_Disp[0] = Temp_Ctorl[0];
Temp_Disp[1] = Temp_Ctorl[1];
从参数设置界面切换到数据界面
当按键按下后,Seg_Disp_Mode数值变为0时,界面从参数设置界面切换到数据界面。
这里需要注意参数设置界面退出时要对设置的上下限进行合理性检查且参数设置界面在退出时设置的参数值才生效。
else//从参数界面切换到数据界面
{
if(Temp_Disp[0] >= Temp_Disp[1])//合理型检查
{
Temp_Ctorl[0] = Temp_Disp[0];
Temp_Ctorl[1] = Temp_Disp[1];
}
}
调试时发现的问题
从参数设置界面切换回数据界面时,温度上限参数仍然处于点亮状态。
为解决此问题,可以在每次调用数据显示界面时,对Seg_Buf[3]和Seg_Buf[4]进行更改。
if(Seg_Disp_Mode == 0)//数据界面
{
Seg_Buf[0] = 11;//C
Seg_Buf[3] = 10;
Seg_Buf[4] = 10;
Seg_Buf[6] = Temperature /10 % 10;
Seg_Buf[7] = Temperature % 10;
}
S5:“参数切换”按键
该按键是在参数设置界面下才有用的,所以,要对当前界面先进行判断,当Seg_Disp_Mode为1时,即处于参数设置界面,可以实现参数切换功能。
引入一个变量Temp_Index使其在0和1之间变换,用于对上下限的选择。
bit Temp_Index = 1;//参数数组指针
按键S5每按下一次,Temp_Index变化一次,因为Temp_Index是只在0和1之间变换的bit型数据,所以可以用异或来运算。
Temp_Index ^= 1;
S6:“加”按键 和 S7:"减"按键
加减按键在参数设置界面下才有用的,所以,还是要对当前显示的界面先进行判断。
且在该界面下,需要实现温度参数的变化,即对Temp_Disp进行更改。而加减是对上限参数的操作还是下限参数的操作在上一步中已经实现了,这里只要直接调用指向温度参数的变量Temp_Index即可。
这里还要注意的一点是温度的边界。
在减中,Temp_Disp为无符号char型,最大值为255,最小值为0,当Temp_Disp为0时再减1就会变成255,但是255超出了显示范围,且不符合当前情境下的逻辑。所以,当Temp_Disp自减1时,给它重新赋值为0,实现的是,减到0之后不会再变小。加处理与之类似,不再赘述。
case 6:
if(Seg_Disp_Mode == 1)
{
if(++Temp_Disp[Temp_Index] == 100)
Temp_Disp[Temp_Index] = 99;
}
break;
case 7:
if(Seg_Disp_Mode == 1)
{
if(--Temp_Disp[Temp_Index] == 255)
Temp_Disp[Temp_Index] = 0;
}
break;
调试时发现的问题
调试时发现,无法实现上图功能。那就在数据界面切换到参数设置界面时加入对Temp_Index重置的操作。
case 4:
Seg_Disp_Mode ^= 1;
if(Seg_Disp_Mode == 1)//从数据界面切换到参数设置界面
{
Temp_Index = 1;
Temp_Disp[0] = Temp_Ctorl[0];
Temp_Disp[1] = Temp_Ctorl[1];
}
LED指示灯功能部分
LED的部分比较简单,实现点亮只要对主模块中的Led显示数据存放数组ucLed中的数进行更改即可。
L1、L2、L3
由题目可知,实时温度和设定温度进行比较后控制对应LED的点亮与否。当条件为真时,对应LED点亮。
ucLed[0] = (Temperature > Temp_Ctorl[0]);
ucLed[1] = (Temperature <= Temp_Ctorl[0] && Temperature >= Temp_Ctorl[1]);
ucLed[2] = (Temperature < Temp_Ctorl[1]);
L4
不同于L1、L2和L3的是,L4不是在比较结果下进行操作的。
L4的点亮与否对应的是参数设置界面中的合理性检查是否通过。当合理性检查通过时,L4熄灭;反之,点亮。且需要注意的是,L4在下一次通过合理性检查前都保持点亮状态。
定义一个bit型变量,用于标记当前是否通过合理性检查。
bit Error_Flag; //错误标志位
L4的点亮状况与是否通过合理性检测有关,即与错误标志位有关。当通过合理性检查是,L4给0,熄灭;反之,给1,点亮。
ucLed[3] = Error_Flag;
回到与合理性检查相关的部分,根据上述添加对错误标志位的处理。
else//从参数界面切换到数据界面
{
if(Temp_Disp[0] >= Temp_Disp[1])//合理性检查
{
Error_Flag = 0;
Temp_Ctorl[0] = Temp_Disp[0];
Temp_Ctorl[1] = Temp_Disp[1];
}
else Error_Flag = 1;
}
DAC输出功能部分
由题可知,DAC输出中的三个判断条件与L1、L2和L3正好对应。且三个条件有且仅有一个是成立的。所以可以用一个for循环来以此取出当前LED的点亮状态,当该LED点亮时,即对应的条件成立,则其他条件不成立,所以只要处理完当前条件下的DAC输出即可。
for(i=0;i<3;i++)
{
if(ucLed[i] == 1)
{
break;
}
}
调用DAC输出函数Da_Write()时需要注意的是,该函数参数为最小值为0最大值为255的unsigned char型数据。而当前题目需要的是最小为0,最大为4的DAC输出。芯片工作电压为5,可以看出成是在0-5的范围内取4、3、2三个数。
要将0-5等比例放大到0-255为51倍。
再看DAC输出值与对应条件下ucLed的关系。DAC输出4时,ucLed[0]为1;DAC输出3时,ucLed[1]为1;DAC输出2时,ucLed[2]为1。关系为4-i。
Da_Write(51*(4-i));
最终代码
User文件
main.c
/* 头文件声明区 */
#include "iic.h"
#include "onewire.h"
#include <Init.h> // 初始化底层驱动专用头文件
#include <Key.h> // 按键底层驱动专用头文件
#include <Led.h> // LED底层驱动专用头文件
#include <STC15F2K60S2.H> // 单片机寄存器专用头文件
#include <Seg.h> // 数码管底层驱动专用头文件
/* 变量声明区 */
unsigned char Key_Val, Key_Down, Key_Old, Key_Up; // 按键状态变量
unsigned char Seg_Buf[8] = {10, 10, 10, 10, 10, 10, 10, 10}; // 数码管显示数据
unsigned char Seg_Point[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // 数码管小数点数据
unsigned char Seg_Pos; // 数码管扫描位置
unsigned char ucLed[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // LED显示数据
unsigned int Slow_Down; // 减速计数器
bit Seg_Flag, Key_Flag; // 数码管和按键的标志位
unsigned int Time_1s; // 1秒钟计数器
unsigned int Freq; // 频率计算变量
unsigned int Sys_Tick; // 系统时钟计数
bit Seg_Disp_Mode; //数码管显示模式变量 0-数据界面 1-参数设置界面
unsigned char Temperature; //实时温度变量
unsigned char Temp_Disp[2] = {30,20};//温度变量显示数组
unsigned char Temp_Ctorl[2] = {30,20};//温度变量控制数组
bit Temp_Index = 1;//参数数组指针
bit Error_Flag; //错误标志位
/* 键盘处理函数 */
void Key_Proc()
{
if (Key_Flag)return;
Key_Flag = 1; // 设置标志位,防止重复进入
Key_Val = Key_Read(); // 读取按键值
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:
Seg_Disp_Mode ^= 1;
if(Seg_Disp_Mode == 1)//从数据界面切换到参数设置界面
{
Temp_Index = 1;
Temp_Disp[0] = Temp_Ctorl[0];
Temp_Disp[1] = Temp_Ctorl[1];
}
else//从参数界面切换到数据界面
{
if(Temp_Disp[0] >= Temp_Disp[1])//合理型检查
{
Error_Flag = 0;
Temp_Ctorl[0] = Temp_Disp[0];
Temp_Ctorl[1] = Temp_Disp[1];
}
else Error_Flag = 1;
}
break;
case 5:
if(Seg_Disp_Mode == 1) Temp_Index ^= 1;
break;
case 6:
if(Seg_Disp_Mode == 1)
{
if(++Temp_Disp[Temp_Index] == 100)
Temp_Disp[Temp_Index] = 99;
}
break;
case 7:
if(Seg_Disp_Mode == 1)
{
if(--Temp_Disp[Temp_Index] == 255)
Temp_Disp[Temp_Index] = 0;
}
break;
}
}
/* 信息处理函数 */
void Seg_Proc()
{
if (Seg_Flag)return;
Seg_Flag = 1; // 设置标志位
Temperature = rd_temperature();
if(Seg_Disp_Mode == 0)//数据界面
{
Seg_Buf[0] = 11;//C
Seg_Buf[3] = 10;
Seg_Buf[4] = 10;
Seg_Buf[6] = Temperature /10 % 10;
Seg_Buf[7] = Temperature % 10;
}
else//参数设置界面
{
Seg_Buf[0] = 12;//P
Seg_Buf[3] = Temp_Disp[0] / 10 % 10;
Seg_Buf[4] = Temp_Disp[0] % 10;
Seg_Buf[6] = Temp_Disp[1] / 10 % 10;
Seg_Buf[7] = Temp_Disp[1] % 10;
}
}
/* 其他显示函数 */
// LED显示处理函数,这里没有具体实现。
void Led_Proc()
{
unsigned char i;
/*Led*/
ucLed[0] = (Temperature > Temp_Ctorl[0]);
ucLed[1] = (Temperature <= Temp_Ctorl[0] && Temperature >= Temp_Ctorl[1]);
ucLed[2] = (Temperature < Temp_Ctorl[1]);
ucLed[3] = Error_Flag;
/*DAC*/
for(i=0;i<3;i++)
{
if(ucLed[i] == 1)
{
Da_Write(51*(4-i));
}
break;
}
}
/* 定时器0初始化函数 */
// 初始化定时器0,用于产生1ms的时钟中断。
void Timer0_Init(void)
{
AUXR &= 0x7F; // 设置定时器时钟12T模式
TMOD &= 0xF0; // 设置定时器模式为16位定时器
TMOD |= 0x05;
TL0 = 0; // 设置定时器初始值
TH0 = 0; // 设置定时器初始值
TF0 = 0; // 清除TF0标志位
TR0 = 1; // 启动定时器
}
/* 定时器1初始化函数 */
// 初始化定时器1,用于产生1ms的时钟中断,并允许中断。
void Timer1_Init(void)
{
AUXR &= 0xBF; // 设置定时器时钟12T模式
TMOD &= 0x0F; // 设置定时器模式为16位定时器
TL1 = 0x18; // 设置定时器初始值
TH1 = 0xFC; // 设置定时器初始值
TF1 = 0; // 清除TF1标志位
TR1 = 1; // 启动定时器
ET1 = 1; // 使能定时器1中断
EA = 1; // 开启全局中断
}
/* 定时器1中断服务函数 */
// 定时器1的中断服务函数,用于更新系统时钟、处理按键和数码管显示。
void Timer1_Isr(void) interrupt 3
{
/*数码管400ms的减速*/
if (++Slow_Down == 400)
{
Seg_Flag = Slow_Down = 0; // 更新数码管显示标志位
}
/*按键10ms的减速*/
if (Slow_Down % 10 == 0)
{
Key_Flag = 0; // 更新按键处理标志位
}
/*1s的计数*/
if (++Time_1s == 1000)
{
Time_1s = 0; // 重置1秒钟计数器
Freq = TH0 << 8 | TL0; // 计算频率
TH0 = 0; // 重置定时器0的值
TL0 = 0;
}
/*信息处理*/
Seg_Disp(Slow_Down % 8, Seg_Buf[Slow_Down % 8], Seg_Point[Slow_Down % 8]); // 更新数码管显示
Led_Disp(Slow_Down % 8, ucLed[Slow_Down % 8]); // 更新LED显示
}
void Delay750ms() //@12.000MHz
{
unsigned char i, j, k;
i = 35;
j = 51;
k = 182;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
/* 主函数 */
// 系统初始化,设置定时器和串口,然后进入主循环。
void main()
{
rd_temperature();
Delay750ms();
Sys_Init(); // 系统初始化
Timer1_Init(); // 初始化定时器1
while (1)
{
Key_Proc(); // 处理按键
Seg_Proc(); // 更新数码管显示
Led_Proc(); // 更新LED显示
}
}
Driver文件
Init.c
#include "init.h"
void Sys_Init()
{
P0 = 0xff;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
P0 = 0x00;
P2 = P2 & 0x1f | 0xA0;
P2 &= 0x1f;
}
LED.c
#include "LED.h"
void LED_Disp(unsigned char addr,enable)
{
static unsigned char temp = 0x00;
static unsigned char temp_old = 0xff;
if(enable) temp |= 0x01 << addr;
else temp &= ~(0x01 << addr);
if(temp != temp_old)
{
P0 = ~temp;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
temp_old = temp;
}
}
Seg.c
#include "Seg.h"
code unsigned char seg_dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x88,0xc6,0x8c};//0-9为数字,10为熄灭,11为A,12为C,13为P
code unsigned char seg_wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void Seg_Disp(unsigned char wela,dula,point)
{
P0 = 0xff;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
P0 = seg_wela[wela];
P2 = P2 & 0x1f | 0xc0;
P2 &= 0x1f;
P0 = seg_dula[dula];
if(point) P0 &= 0x7f;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
}
Key.c
#include <Key.h>
unsigned char Key_Read()
{
unsigned char temp = 0;
if (P33 == 0) temp = 4;
if (P32 == 0) temp = 5;
if (P31 == 0) temp = 6;
if (P30 == 0) temp = 7;
return temp;
}
onewire.c
#include "onewire.h"
sbit DQ = P1 ^ 4;
//
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
float rd_temperture(void)
{
unsigned char low,high;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low = Read_DS18B20();
high = Read_DS18B20();
return ((high << 8) | low) / 16.0;
}
iic.c
#define DELAY_TIME 10
#include "iic.h"
#include "intrins.h"
sbit scl = P2 ^ 0;
sbit sda = P2 ^ 1;
//
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--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
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;
}
//
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;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
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);
}
void Da_Write(unsigned char dat)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x41);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
结语
至此本试题代码部分的笔记就完成了,有错误的地方感谢大家指出。