【蓝桥杯—单片机】第十一届省赛真题代码题解题笔记 | 省赛 | 真题 | 代码题 | 刷题 | 笔记

第十一届省赛真题代码部分

  • 前言
  • 赛题代码思路笔记
    • 竞赛板配置
      • 内部振荡器频率设定
      • 键盘工作模式跳线
      • 扩展方式跳线
    • 建立模板
    • 明确设计要求和初始状态
    • 显示功能部分
      • 数据界面
        • 第一部分
        • 第二部分
        • 第三部分
        • 调试时发现的问题
      • 参数设置界面
        • 第一部分
        • 第二部分和第四部分
        • 第三部分和第五部分
    • 按键功能部分
      • 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{}
}

数据界面

下面来完成第一个界面的编写。
在这里插入图片描述

由题目可知,数据界面由三部分组成:

  1. 第一部分为提示符部分,是数码管的第一位,在该界面下全程显示字母C。
  2. 第二部分为熄灭部分,是数码管的第2到6位,在该界面下全程熄灭。
  3. 第三部分为实时温度显示界面,是数码管的第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();

参数设置界面

在这里插入图片描述
由题目可知,数据界面由五部分组成:

  1. 第一部分为提示符部分,是数码管的第一位,在该界面下全程显示字母P。
  2. 第二部分为熄灭部分,是数码管的第2到3位,在该界面下全程熄灭。
  3. 第三部分为温度上限参数部分,是数码管的第4到5位。
  4. 第四部分为熄灭部分,是数码管的第6位,在该界面下全程熄灭。
  5. 第五部分为温度下限参数部分,是数码管的最后两位。
第一部分

标识部分对应的数码管为第一位,更改主模块中对应数码管控制列表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;//CSeg_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;//CSeg_Buf[3] = 10;Seg_Buf[4] = 10;Seg_Buf[6] = Temperature /10 % 10;Seg_Buf[7] = Temperature % 10;}else//参数设置界面{Seg_Buf[0] = 12;//PSeg_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();	// 初始化定时器1while (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();
}

结语

至此本试题代码部分的笔记就完成了,有错误的地方感谢大家指出。

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

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

相关文章

寒假2.7

题解 web&#xff1a;[HCTF 2018]WarmUp 打开是张表情包 看一下源代码 访问source.php&#xff0c;得到完整代码 代码审计 <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist ["source">"source.p…

python康威生命游戏的图形化界面实现

康威生命游戏&#xff08;Conway’s Game of Life&#xff09;是由英国数学家约翰何顿康威&#xff08;John Horton Conway&#xff09;在1970年发明的一款零玩家的细胞自动机模拟游戏。尽管它的名字中有“游戏”&#xff0c;但实际上它并不需要玩家参与操作&#xff0c;而是通…

【数据结构】链表应用-链表重新排序

重新排序 反转链表预期实现思路解题过程code力扣代码核心代码完整代码 总结 删除链表中间节点代码解惑 链表重新排序题目描述解题思路解题过程复杂度代码力扣代码完整代码 反转链表 预期实现 思路 你选用何种方法解题&#xff1f; 我选用了迭代法来反转链表。这是一种经典且高…

pytest-xdist 进行多进程并发测试!

在软件开发过程中&#xff0c;测试是确保代码质量和可靠性的关键步骤。随着项目规模的扩大和复杂性的增加&#xff0c;测试用例的执行效率变得尤为重要。为了加速测试过程&#xff0c;特别是对于一些可以并行执行的测试用 例&#xff0c;pytest-xdist 提供了一种强大的工具&…

mysql8安装时提示-缺少Microsoft Visual C++ 2019 x64 redistributable

MySQL8.0安装包mysql-8.0.1-winx64进行安装&#xff0c;提示&#xff1a;This application requires Visual Studio 2019 x64Redistributable, Please install the Redistributable then runthis installer again。出现这个错误是因为我们电脑缺少Microsoft Visual C 这个程序&…

【MySQL】深度理解事务的隔离性:全面讲解事务的四种隔离级别

**前言&#xff1a;**上节内容我们主要说了如果没有设置保存点&#xff0c; 也可以回滚&#xff0c;但是只能回滚到事务的开始。直接使用rollback的前提是事务还没有提交。并且如果一个事务被提交了&#xff0c;就不可以回退。同时我们也可以使用savepoint设置回滚点。 可以自己…

项目实战 —— HTTP服务器设计与实现

目录 一&#xff0c;项目介绍 二&#xff0c;背景知识补充 2.1 http特点 2.2 URI&#xff0c;URL&#xff0c;URN 2.3 http请求方法 三&#xff0c;前置功能实现 3.1 日志编写 3.2 封装相关套接字 3.3 http请求结构设计 3.4 http响应结构设计 3.5 http服务器主体逻辑…

GitHub Copilot:智能助手觉醒

GitHub Copilot: The agent awakens - The GitHub Blog github copilot 官方文档刚刚宣布支持 agent 模式&#xff01; 这一模式和之前的 chat 方式不同&#xff0c;类似于 cursor 可以根据需求直接运行、调试和修改代码 这一模式在 preview 版本可以使用&#xff0c;并且需…

网络安全威胁框架与入侵分析模型概述

引言 “网络安全攻防的本质是人与人之间的对抗&#xff0c;每一次入侵背后都有一个实体&#xff08;个人或组织&#xff09;”。这一经典观点概括了网络攻防的深层本质。无论是APT&#xff08;高级持续性威胁&#xff09;攻击、零日漏洞利用&#xff0c;还是简单的钓鱼攻击&am…

深入浅出谈VR(虚拟现实、VR镜头)

1、VR是什么鬼&#xff1f; 近两年VR这次词火遍网上网下&#xff0c;到底什么是VR&#xff1f;VR是“Virtual Reality”&#xff0c;中文名字是虚拟现实&#xff0c;是指采用计算机技术为核心的现代高科技手段生成一种虚拟环境&#xff0c;用户借助特殊的输入/输出设备&#x…

postman免登录版本,实测可用(解决一直卡在登录界面无法进入的问题)

一、背景 2025今年开工后&#xff0c;打开postman&#xff0c;一直提示需要登录&#xff0c;但是一直卡在登录界面&#xff0c;好几个人的postman都是这样的情况&#xff0c;不知道是什么原因。 折腾几小时无果&#xff0c;网上下载了各种版本都试了&#xff0c;最新的版本也…

Unity中Spine骨骼动画完全指南:从API详解到避坑实战

Unity中Spine骨骼动画完全指南&#xff1a;从API详解到避坑实战 一、为什么要选择Spine&#xff1f; Spine作为专业的2D骨骼动画工具&#xff0c;相比传统帧动画可节省90%资源量。在Unity中的典型应用场景包括&#xff1a; 角色换装系统&#xff08;通过插槽替换部件&#xf…

HTTP异步Client源码解析

我们知道Netty作为高性能通信框架&#xff0c;优点在于内部封装了管道的连接通信等操作&#xff0c;用户只需要调用封装好的接口&#xff0c;便可以很便捷的进行高并发通信。类似&#xff0c;在Http请求时&#xff0c;我们通过调用HttpClient&#xff0c;内部使用java NIO技术&…

Golang:Go 1.23 版本新特性介绍

流行的编程语言Go已经发布了1.23版本&#xff0c;带来了许多改进、优化和新特性。在Go 1.22发布六个月后&#xff0c;这次更新增强了工具链、运行时和库&#xff0c;同时保持了向后兼容性。 Go 1.23 的新增特性主要包括语言特性、工具链改进、标准库更新等方面&#xff0c;以下…

无界构建微前端?NO!NO!NO!多系统融合思路!

文章目录 微前端理解1、微前端概念2、微前端特性3、微前端方案a、iframeb、qiankun --> 使用比较复杂 --> 自己写对vite的插件c、micro-app --> 京东开发 --> 对vite支持更拉跨d、EMP 方案--> 必须使用 webpack5 --> 很多人感觉不是微前端 --> 去中心化方…

SQL Server 数据库迁移到 MySQL 的完整指南

文章目录 引言一、迁移前的准备工作1.1 确定迁移范围1.2 评估兼容性1.3 备份数据 二、迁移工具的选择2.1 使用 MySQL Workbench2.2 使用第三方工具2.3 手动迁移 三、迁移步骤3.1 导出 SQL Server 数据库结构3.2 转换数据类型和语法3.3 导入 MySQL 数据库3.4 迁移数据3.5 迁移存…

RabbitMQ深度探索:死信队列

死信队列产生背景&#xff1a; RabbitMQ 死信队列俗称 备胎队列&#xff1a;消息中间件因为某种原因拒收该消息后&#xff0c;可以转移到私信队列中存放&#xff0c;死信队列也可以有交换机和路由 key 等 生产死信队列的原因&#xff1a; 消息投递到 MQ 存放&#xff0c;消息已…

蓝桥算法基础2

位运算 按位与&#xff0c;x&1x%2.因为1不论和几位二进制与&#xff0c;都只有最后一位为1&#xff0c;前面都是0&#xff0c;那么&前面也都为0&#xff0c;只有最后一位&#xff0c;若为1那么2的0次方为1&#xff0c;该数一定为奇数&#xff0c;与取余结果同&#xff…

B站自研的第二代视频连麦系统(上)

导读 本系列文章将从客户端、服务器以及音视频编码优化三个层面&#xff0c;介绍如何基于WebRTC构建视频连麦系统。希望通过这一系列的讲解&#xff0c;帮助开发者更全面地了解 WebRTC 的核心技术与实践应用。 背景 在文章《B站在实时音视频技术领域的探索与实践》中&#xff…

redis之AOF持久化过程

流程图 在redis.conf文件中配置appendonly为yes则开启aof持久化机制 #开启aof持久化&#xff0c;默认关闭为no appendonly no也可以在命令行开启 aof刷盘策略 #每个写操作都会同步刷盘。 appendfsync always #执行命令后先放入aof缓冲区&#xff0c;每秒钟将缓冲区数据刷盘…