[蓝桥杯单片机] - 蓝桥杯单片机CT107D竞赛板各模块代码分析
蓝桥杯笔记
“免责声明” ( •̀ ω •́ )✧
代码未全部验证,也许存在BUG,如发现错误欢迎指正,不愿意指正那就当作没看见也行
所有说明文字仅代表笔者个人想法
修正日志
从2023-02开始的修正日志:
date | brief |
---|---|
2023-02-02 22:41 |
IIC/AT24C02 : 读取函数误写为IIC_Write ,修正为IIC_Read
|
2023-02-02 22:53 |
IIC/AT24C02 : 发送函数数据参数unsigned char data不建议命名为data ,data为C51保留字,使用会报错,建议改为dat
|
2023-03-15 21:45 |
修正超声波代码中#inlcude "intrins.h" 中写错的include ;修正超声波代码中的管脚定义命名错误(sbit ULTX = P1^1; to ULRX)
|
文章目录
- 蓝桥杯笔记
- “免责声明” ( •̀ ω •́ )✧
- 修正日志
- CT107D硬件概况
- 程序
- 基础设备控制
- 数码管驱动
- 按键
- § 独立按键
- § 矩阵按键
- IIC
- § AT24C02
- § AD/DA
- 18B20--One Wire
- DS1302
- Uart串口
- 超声波
- 频率测量
- 系统结构
- Project ψ(._. )>
- Main函数 ψ(._. )>
- 时间控制 ψ(._. )>
CT107D硬件概况
首先是国信长天CT107D开发板的硬件概况,怎么说呢,一言难尽,268软妹币,血亏
其中虽然板上留有一些外设的拓展口,但实际上是不存在附带模块的,没错,268的板子连LED点阵都不带。 ̄へ ̄
程序
基础设备控制
由于开发板的设计,这块板上点个灯稍微有一丢丢复杂,根据电路结构,8颗LED需要通过74HC138去操作。
P2 = (P2&0x1F)|0xA0;
/*
0x80: 100 --4-->Y4C: LED灯
0xA0: 101 --5-->Y5C: 挂在ULN2003上的外设
0xC0: 110 --6-->Y6C: 数码管位选
0xE0: 111 --7-->Y7C: 数码管段选
*/
P0 = ctrl;
P2 &= 0x1F;
[目录](# “免责声明” ( •̀ ω •́ )✧)
数码管驱动
位选为Y6C选定的575,段选则为Y7C,即P25-P27组成6和7;
即110和111,那P2就是C0H和E0H;
C0位选,E0段选;
void Display(void)
{static dispcom;/*消隐*/P0 = 0xFF; //芯片选定到P0复制完成的间隙会产生残影,故先赋值P2 = (P2&0x1F)|0xE0; // 控制选定的575且不改变P2其他管脚状态P0 = 0xFF;P2 &= 0x1F; // 关闭选定/*位选*/P2 = (P2&0x1F)|0xC0;P0 = 0x01<<dispcom; // 循环显示P2 &= 0x1F;/*段选*/P0 = 0xFF;P2 = (P2&0x1F)|0xE0;P0 = DispTab[DispBuf[dispcom]]; // DispTab : 共阳数码管段选码 DispBuf : 显示缓冲区P2 &= 0x1F;dispcom++;if(dispcom==8)dispcom = 0; // 防止dispcom超出范围
}
按键
§ 独立按键
J5接至BTN,P3低4位控制4个按键;
unsigned char KeyValue = 0xFF; //全局变量记录按键值
------------------------------------------------------------
void BTN(void)
{/*变量*/static unsigned char keyvalue; //临时记录键值static unsigned char keypress; //记录扫描按下次数static bit keyfree = 1; //按键按下与否unsigned char temp; //为方便判断/*扫描*/P3 |= 0x0F; //将P3口低4位设为高temp = P3&0x0F; //取P3口低4位,其余为0,赋给temp,便于判断/*消抖*/if(temp!=0x0F)keypress++; //如果temp不等于0x0F,说明有键按下else keypress = 0; //如果在keypress加到5之前temp回归0x0F,就不算作按下了/*识别*/if(keypress==5&&keyfree) //如果按键按下持续5个扫描,且按键在未按下的状态,就算按键按下了{keypress = 0; //归0keypresskeyfree = 0; //将按键状态设为按下,即不自由(free)(~ ̄▽ ̄)~switch(temp) //按键识别{case 0x07:keyvalue = 4;break;case 0x0B:keyvalue = 5;break;case 0x0D:keyvalue = 6;break;case 0x0E:keyvalue = 7;break;}}/*松手检测*/if(temp==0x0F&&keyfree==0) //若temp回归0x0F,且按键状态为按下,说明松手了,返回键值{keyfree = 1; //松手后将keyfree改为1,它免费了(~ ̄▽ ̄)~KeyValue = keyvalue;}else Keyvalue = 0xFF; //其他情况返回0xFF
}
§ 矩阵按键
J5接至KBD,P3(不存在P36、P37)和P42、P44共同控制按键;
unsigned char KeyValue = 0xFF; //全局变量记录按键值
------------------------------------------------------------
void KBD(void)
{/*变量*/unsigned char S1=0x00,S2=0x00; //按键键值的行,列数据static unsigned char keyvalue; //临时记录键值static unsigned char keypress; //记录扫描按下次数static bit keyfree = 1; //按键按下与否unsigned char temp = 0xFF; //临时存放扫描数据/*扫描*/P3 = 0x0F; //将P3口低4位设为高P42 = 0;P44 = 0; //P3高2位和P42P44组成高4位设为低temp = (P3&0x0F);/*消抖*/if(temp!=0x0F)keypress++; //如果P3低4位不等于0x0F,说明疑似有键按下//此处容易出现P3&0x0F!=0x0F这类错误else keypress = 0; //如果在keypress加到5之前P3低4位回归0x0F,就不算作按下了/*识别*/if(keypress==5&&keyfree) //如果按键按下持续5个扫描,且按键在未按下的状态,就算按键按下了{keypress = 0; //归0keypresskeyfree = 0; //将按键状态设为按下,即不自由(free)(~ ̄▽ ̄)~S1 = temp; //记录按键行值P3 = 0xF0;P42 = 1;P44 = 1; //反转扫描,确定列if(!P42) S2 = 0xB0; //如果是P42=0;说明按下的键就在这列else if(!P44) S2 = 0x70; //同上/*这一句一定要用else if 否则S8-S11失效如果不用else if,!P42确实为1,但是!P44为0会导致else的执行覆盖S2*/else S2 = temp; //否则数据在P3中,记录列switch(S1|S2) //按键识别{/*S4~S7*/case 0x77:keyvalue = 4;break;case 0x7B:keyvalue = 5;break;case 0x7D:keyvalue = 6;break;case 0x7E:keyvalue = 7;break;/*S8~S11*/case 0xB7:keyvalue = 8;break;case 0xBB:keyvalue = 9;break;case 0xBD:keyvalue = 10;break;case 0xBE:keyvalue = 11;break;/*S12~S15*/case 0xD7:keyvalue = 12;break;case 0xDB:keyvalue = 13;break;case 0xDD:keyvalue = 14;break;case 0xDE:keyvalue = 15;break;/*S15~S19*/case 0xE7:keyvalue = 16;break;case 0xEB:keyvalue = 17;break;case 0xED:keyvalue = 18;break;case 0xEE:keyvalue = 19;break;}}/*松手检测*/if(temp==0x0F&&keyfree==0) //若P3回归0x0F,且按键状态为按下,说明松手了,返回键值{keyfree = 1;KeyValue = keyvalue;}else Keyvalue = 0xFF; //其他情况返回0xFF
}
IIC
§ AT24C02
不用从头写起,但是需要自己写最后使用的发送和接收函数;
数据包给出了启动停止应答等操作的函数,只需要知道IIC通信的时序或者步骤即可;
我们需要从数据手册中得到这个时序;
在AT24C02的数据手册中,我们可以在Read Operation下面找到上面这张图。乍一看看不出到底Byte Write,有多少个步骤,但实际上重点有两个图表,还包括上面那个;如下:
Figure 7表明:MSB,R/W,LSB都属于同一个字节,而在赛方给出的IIC参考程序中,有两种操作函数,电平变化和字节传输;
所以将Figure 8划分一下也变得非常简单:
易得它的顺序是
IIC_Start();
IIC_SendByte(?);
IIC_WaitAck();
IIC_SendByte(?);
IIC_WaitAck();
IIC_SendByte(?);
IIC_WaitAck();
IIC_Stop();
接下来需要知道“?”里填啥?
首先是DEVICE ADDRESS,其实甚至可以从图中出答案(当然,给出的数据里也有),图中的就是正确的(必须的呀),即0xA0;板上AT24C02芯片地址为000;
第二个发送的字节是WORD ADDRESS,即数据要写在AT24C02的哪里?这个位置是由使用情况决定的,于是设置一个输入参数,add,最后是发送的数据,自然,也是参数;
所以IIC写函数最后是:
void IIC_Write(unsigned char add,unsigned char dat)
{IIC_Start();IIC_SendByte(0xA0); //或者写给出的SlaveAddrWIIC_WaitAck();IIC_SendByte(add); IIC_WaitAck(); IIC_SendByte(dat);IIC_WaitAck();IIC_Stop();
}
那么读函数亦是如此啦;
当然,我们看到读取并不简单,它有三种模式;即 Current Address Read、 Random Read、 Sequential Read.
类型 | 描述 |
---|---|
Current Address Read | 未断电时,读上一次读的地址(即当前地址)。断电后,地址归为0x00 |
Random Read | 指定地址读取(这是我们需要的) |
Sequential Read | 连续读,先这样,再这样,再那样,就可以一个地址接下一个地址连续读 |
显然,传输步骤是:
IIC_Start();
IIC_SendByte(?); //看图,"?"应该是0xA0(或SlaveAddrW);
IIC_WaitAck();
IIC_SendByte(?); //需要读出的地址,设置形参add
IIC_WaitAck();
IIC_Start();
IIC_SendByte(?); //0xA1
IIC_WaitAck();
IIC_RecByte(); //这是数据,设置形参RecData
IIC_SendAck(?); //主机应答(是1哦)
IIC_Stop();
所以,最后是:
unsigned char IIC_Read(unsigned char add)
{unsigned char RecData;/*伪写*/IIC_Start();IIC_SendByte(0xA0); IIC_WaitAck();IIC_SendByte(add); IIC_WaitAck();/*读取*/IIC_Start();IIC_SendByte(0xA1); IIC_WaitAck();RecData = IIC_RecByte(); IIC_SendAck(1); IIC_Stop();return RecData;
}
AT24C02的读写差不多就是这样了。
§ AD/DA
对于DA过程,S直接是start;而对于AD(对应IIC的读)需要包含伪写;
首先看图中各个字节如何划分;
对于板上PCF8591来讲,地址位字节的高7位都是固定的,不固定的只有读写位,看图也可知写为低位有效。所以对写来说地址字节就是0x90;那么在上面的流程图中,ADDRESS和后面那个数字位肯定是在1个字节中,属于同一步操作;
控制字:
最低两位:通道,AD输入只能在0通道,DA输出可以选择0-3通道 DA输出只能在0通道,AD输入可以选择0-3通道;
2号位:自动递增位,这里用不到,不管,设为0;
3号位:固定为0;(最高位同)
4,5号位:输入模式,AD输入时的输入模式,我们需要一一对应,直接设为00;
6号位:输出使能,DA输出时设为1;
了解这个之后就可以直接看最上面两张图写出大致步骤了;
IIC_Start(void);
IIC_Stop(void);
IIC_WaitAck(void);
IIC_SendAck(bit ackbit);
IIC_SendByte(unsigned char byt);
IIC_RecByte(void);
//DA输出
IIC_Start();
IIC_SendByte(0x90); //输出对应写,低电平有效,故为90
IIC_WaitAck();
IIC_SendByte(0x40); //输出使能,通道0输出
IIC_WaitAck();
IIC_SendByte(DA_data); //输出大小由参数确定
IIC_WaitAck();
IIC_Stop();
同理也可得到AD转换的程序;
//AD输入
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(channal); //关闭输出使能,选择通道输入
IIC_WaitAck();
/*以上为伪写*/
IIC_Start();
IIC_SendByte(0x91); //输出对应读,高电平有效,故为91
IIC_WaitAck();
AD_data = IIC_RecByte(); //接收数据,存入参数AD_data
IIC_SendAck(1); //接收时主机发送应答,和IIC不同,此时应答为1结束
IIC_Stop();
补全函数头和相关的变量定义可以得到完整的AD/DA 程序。
PS:调用AD/DA转换函数时,注意读取的是上一次转换的值。所以必要的时候要调用两遍。
18B20–One Wire
对于18B20,它使用的是onewire总线,由于只有一根线进行通信,其含义基本都是通过电平持续时间来表示的。所以对时间的把控相当严格。
这时要做一件非常重要的事,将原来资料中的延时函数改一下,否则时间就不对了;
//单总线延时函数
//void Delay_OneWire(unsigned int t) //STC89C52RC
//{// while(t--);
//}
------------------------------------------------------------------
void Delay_OneWire(unsigned int t) //STC15F2K60S2
{t *= 12;while(t--);
}
即“简单的硬件条件需要相对复杂的软件来补充”
所以软件会相对复杂;
给出的资料就是这么多了。接下来看数据手册;
温度在18B20中的数据存储位置等信息,但是这些对我们帮助不大,主要是相关的驱动程序都已经给出来了,我们需要知道的是它的读取步骤;
而在这个例子中,步骤就非常明显了;
我们只想让它做两件事,将温度转换为数据,读出数据;
所以在这个例子中有些东西是不需要的,比如Match ROM(匹配ROM),因为我们板上就只有一个18B20而已。自然,send DS18B20 ROM code也是不必要的,取而代之的是Skip ROM (跳过ROM);
于是有如下步骤:
init_ds18b20();
Write_DS18B20(0xCC); //跳过ROM
Write_DS18B20(0x44); //转换命令
Delay_OneWire(20); //等待转换init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE); //读取命令Temp_L = Read_DS18B20(); //从低位读起
Temp_H = Read_DS18B20();
然后是对数据的处理了,前面说到的“用处不大”Figure2现在用处大了,18B20用多种温度的分辨率,默认的是12位分辨率的,也就是11位数据和其余的符号位;在下面这段话中有明确的表述
The sign bits (S) indicate if the temperature is positive or negative: for positive numbers S = 0 and for negative numbers S = 1.
If the DS18B20 is configured for 12-bit resolution, all bits in the temperature register will contain valid data. For 11-bit resolution, bit 0 is undefined. For 10-bit resolution, bits 1 and 0 are undefined, and for 9-bit
resolution bits 2, 1, and 0 are undefined.
Table 1 gives examples of digital output data and the corresponding temperature reading for 12-bit resolution conversions.
在默认12位情况下,数据值是实际温度的16倍;值得一提的是,温度数据在18B20中的存储可以理解为是以补码的形式存储的;
所以,最后,程序可以写成这个样子
unsigned int T_Value = 0x00;
bit T_Symbol = 0;
--------------------------------------------------------------------------
void rd_temperature(void)
{unsigned char Temp_L,Temp_H;unsigned int Temp = 0x00;init_ds18b20();Write_DS18B20(0xCC); Write_DS18B20(0x44); Delay_OneWire(20); init_ds18b20();Write_DS18B20(0xCC);Write_DS18B20(0xBE); Temp_L = Read_DS18B20(); //先低位再高位Temp_H = Read_DS18B20();Temp = Temp_H; //将2byte温度数据组成一个温度值Temp <<= 8;Temp += Temp_L;if(Temp_H&0x80){T_Symbol = 1;Temp = ~Temp + 1; //T_Symbol == 1:负温度}else {T_Symbol = 0; //T_Symbol == 0:正温度}T_Value = Temp/16.0*100+0.5; //除以16.0,或者*0.0625,不能是/16,保留两位小数*100,但T_Value会是真实值的100倍
}
注意点:
- 温度值数据类型一定至少得是unsigned int,不要顺手写成了unsigned char。
- 读取温度数据时是先低位,再高位。
- …
PS:调用温度转换函数时,注意读取的是上一次转换的值。所以必要的时候要调用两遍。
DS1302
DS1302;先看给出的程序资料;
打开下面两个函数,就可以知道Write_Ds1302();是为Write_Ds1302_Byte()和Read_Ds1302_Byte()服务的,知道这点很重要,因为这样一来,我们只需要考虑怎么用这两个函数组成我们使用的函数;(两个函数而已,步骤一定不会太难,事实也正是如此)
实际上,直接用给出的函数读寄存器就可以了;
unsigned char SetRTC[3] = {0x12,0x50,0x59};
unsigned char ReadRTC[3] = {0x00,0x00,0x00};
------------------------------------------------------------------------
void Set_RTC(void)
{Write_Ds1302_Byte(0x8E,0x00); //关闭写保护Write_Ds1302_Byte(0x84,SetRTC[0]); //设置时Write_Ds1302_Byte(0x82,SetRTC[1]); //设置分Write_Ds1302_Byte(0x80,SetRTC[2]); //设置秒Write_Ds1302_Byte(0x8E,0x80); //打开写保护
}void Read_RTC(void)
{/*注意!读和写寄存器是不一样的*/ReadRTC[0] = Read_Ds1302_Byte(0x85); //读时ReadRTC[1] = Read_Ds1302_Byte(0x83); //读分ReadRTC[2] = Read_Ds1302_Byte(0x81); //读秒
}
需要注意的是,需要在数码管上显示的时候,取位数得/16而不是/10;
并且,在使用时,初始化中要先写一次初始值,否则会有意想不到的惊喜效果。即DS1302使用起来应该是这样的:
/*初始化*/
Set_RTC();while(1)
{//在该获取时间时Read_RTC();
}
PS:
头文件声明数组时不需要带上数组长度,这样就可以了。
unsigned char SetRTCData[];
unsigned char ReadRTCData[];
Uart串口
串口使用
串口最简使用方法
- 初始化
- 中断服务函数
- 发送数据/接收数据
/*
STC15F2K60S2 定时器2用作串口1的波特率发生器
*/// 串口1中断服务函数
void UartInit(void) //9600bps@12.000MHz
{SCON = 0x50; //8位数据,可变波特率AUXR |= 0x01; //串口1选择定时器2为波特率发生器AUXR |= 0x04; //定时器2时钟为Fosc,即1TT2L = 0xC7; //设定定时初值T2H = 0xFE; //设定定时初值AUXR |= 0x10; //启动定时器2ES = 1;EA = 1;
}// UART 中断服务程序
void Uart() interrupt 4
{uchar i = 0; //i用于存储接收数据if (RI){RI = 0; //清除RI位i = SBUF; //P0显示串口数据}
}// 发送字符串
void SendString(char *s)
{while (*s != '\0') //检测字符串结束标志{SBUF = *s++;while(TI==0);TI = 0;}
}
- 利用stc-isp软件生成初始化程序:
RI为接收中断标志位,即RI==1时表示有被数据写入SBUF。
TI为发送标志位,TI==1时表示数据已发送。(RI=1或者TI=1都可以触发串口中断,进入中断函数)
SCON寄存器详情:
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | |||
---|---|---|---|---|---|---|---|---|---|---|
SCON | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI | 98H | |
位地址 | 9FH | 9EH | 9DH | 9CH | 9BH | 9AH | 99H | 98H |
超声波
超声波测距过程:发送声波,计时,接收声波,计算距离;
没有关于超声波有用的信息给出,需要记住超声波模块大致的使用过程;
1. 定义发送和接收管脚
2. 初始化Timer0
3. 定义发送8个40MHz信号函数
4. 定义测距函数
5. 发送信号,计时,接收信号,计算距离
#include "intrins.h"#define Nops {_nop_();_nop_();_nop_();_nop_();_nop_();\
_nop_();_nop_();_nop_();_nop_();_nop_();\
_nop_();_nop_();_nop_();_nop_();_nop_();}sbit ULTX = P1^0;
sbit ULRX = P1^1;unsigned int Distance = 0;
---------------------------------------------------------------
void Timer0Init(void)
{//STC-ISP直接获取任意一个16位计时器初始化函数//需要是12T时钟的;并且将TH0,TL0都设为0;
}void SendUltra()
{unsigned char fre;for(fre=0;fre<8;fre++){ULTX = 1;Nops;Nops;Nops;Nops;Nops;Nops;Nops;Nops;Nops;Nops;ULTX = 0;Nops;Nops;Nops;Nops;Nops;Nops;Nops;Nops;Nops;Nops;}
}void Measure(void)
{unsigned int time = 0;/*发送*/SendUltra(); //发送信号TR0 = 1; //定时器0开始计时/*接收*/while(ULRX==1&&TF0==0); //收到信号且计时器未溢出,对应两种可能--收到回声(ULRX==0)和超时未收到回音(TF==1)TR0 = 0; //定时器0关闭计时/*分类计算*/if(TF0) //如果导致中断了,说明在很长时间内(大概65ms)没有收到回声{TF0 = 0;Distance = 9999; //超时直接加到最远距离}else{time = TH0;time <<= 8;time |= TL0;Distance = (uint)time*0.017;}/*复位*/TL0 = 0x00; //重新设置定时初值TH0 = 0x00;
}
程序中各种时间乱七八糟,要理解这些必须了解的一个东西就是:单片机的时间和速度到底是怎么样的?
时钟周期:又称为震荡周期,是为单片机提供定时信号的震荡源的周期,是单片机最基本的时间单位;状态周期:CPU从一个状态转换到另一状态所需要的时间。简单地说每个状态周期分为两个震荡周期(时钟周期);机器周期:一个机器周期包含六个状态例如,取指令、存储器读、存储器写等。机器周期 = 6个状态周期 = 12个时钟周期。指令周期: 顾名思义,指令周期就是执行一条指令所需的全部时间。程序中用到的nop()函数就只需要一个指令周期;
① 产生信号
用其他语句来对发送的脉冲电平计时肯定不如nop()来得准确,于是,要产生40KHz的方波,就要确定发送信号引脚处于一个状态的时间;
对于STC15F2K60S2(1T高速芯片),一个指令周期就是一个时钟周期,即 T n o p = 1 / 12 M T_{nop} = 1/12M Tnop=1/12M(s);
而 T U L T X = 1 / 0 = 1 / 40 K = 300 T n o p T_{ULTX = 1/0} = 1/40K = 300T_{nop} TULTX=1/0=1/40K=300Tnop ;
即产生40KHz方波需要ULTX处于高电平150个NOP,低电平150个NOP;
② 计时
12T计时器每12个时钟周期计数+1;即T = 12time/12M (s) = time us;
频率测量
大致步骤:
① 设置计时器0;设置为P34触发的计数器;
② 用计时器0计算500ms内的P34脉冲数;
③ 计算频率
其中TMOD寄存器中 C / T ‾ C/\overline{T} C/T就是控制计数器和定时器切换的“开关”;
和STC-ISP软件中的定时器代码唯一的区别就是打开了这个开关,并且计数槽清零。
//计时器0初始化函数
void Timer0Init(void)
{AUXR &= 0x7F; TMOD &= 0xF0; //保持计时器1的设置不变 11110000 & 计时器1设置TMOD |= 0x04; //设置定时器模式为计数TL0 = 0x00; TH0 = 0x00; TF0 = 0; TR0 = 0;
}//频率测量函数
void FreMeasure(void)
{if(G_Time_1ms%1000==0){Timer0Init();TR0 = 1;}else if(G_Time_1ms%1000==500){TR0 = 0;Frequency = TH0;Frequency <<= 8;Frequency += TL0;Frequency *= 2; //0.5s这么多,1s就是*2TH0 = 0; //重置计数槽TL0 = 0;}
}
注意:测量函数放在计时器1的中断函数中;不然可能在还没执行到测量函数时时间点就过去了;
系统结构
Project ψ(._. )>
小项目来说,笔者倾向于尽量放在同一个源文件,一是自己写着省时省力,不容易出错。二是分太多文件没有必要,很容易出现一个c文件和对应的头文件加起来都没几行代码,另外,如果系统中功能相互勾连,一个c文件中的函数要用到另一个c文件中的变量,头文件包含来包含去,整个项目结构更加混乱。
相反,如果在一个c文件中,你可以将各部分写得模块分明,那也是相当漂亮的。
而对于一个文件显得冗长,则可以在coding过程中将暂时不用管的函数收起(绝大部分编辑器都可以做到这点)。如此一来,coding高效且代码漂亮。
下面给出个人习惯的代码结构参考:
/*------------------------------------------------------------------------------
---------------------- 属于自己的个性区域 -------------------------
------------------------------------------------------------------------------*//*----------------------------头文件及宏定义----------------------------------*/
#include
#define
/*-------------------------------变量定义-------------------------------------*/
uchar
uint
/*-------------------------------函数声明-------------------------------------*/
void Func(void);
/*---------------------------------主函数-------------------------------------*/
void main(void)
{//
}
/*-------------------------------函数定义-------------------------------------*/
// balabalabala
void Func(void);
Main函数 ψ(._. )>
个人认为主函数是一个项目代码结构的体现,一个好的结构其主函数一定是层次分明,一目了然的。(也可能是我个人执念吧o((>ω< ))o)
在下面的主函数控制代码中,以定时器1作为系统的时间管理,利用一系列代表不同时间的变量作为时间标志,类似于定了一个闹钟,闹钟响的时候就去做该做的事,否则就休息(空转while(1));
void main()
{/*初始化*/Init();/*主循环*/while(1){if(TimeFlag_10ms){TimeFlag_10ms = 0;Function();if(TimeFlag_200ms){TimeFlag_200ms = 0;Function();}}}
}
时间控制 ψ(._. )>
void Timer1Sr(void) interrupt 3
{/*时间控制*/Time_1ms++;if(Time_1ms%10==0){TimeFlag_10ms = 1;if(Time_1ms%20==0){TimeFlag_20ms = 1;if(Time_1ms%200==0){TimeFlag_200ms = 1;}}}/*频率测量*///Measure();/*显示刷新*/Display();
}
[蓝桥杯单片机] - 蓝桥杯单片机CT107D竞赛板各模块代码分析相关推荐
- 《蓝桥杯备赛》CT117E嵌入式竞赛板LCD驱动库的使用(带完整源码)
声明:开发板为蓝桥杯CT117E Rev 1.1,资源只用于学习用途 1.蓝桥杯LCD驱动库(官方提供) lcd.c /*程序说明: CT117E嵌入式竞赛板LCD驱动程序软件环境: Keil uVi ...
- 《蓝桥杯CT107D单片机竞赛板》:蜂鸣器模块
蜂鸣器模块 实验简介 实验原理图 实验原理 实验程序 关闭蜂鸣器与继电器 简易报警器原理 实验简介 采用软件方式,使得CT107D单片机竞赛板上的蜂鸣器和继电器分别不发声和不吸附. 实验原理图 实验原 ...
- 【蓝桥杯单片机最全备考资料】真题、代码、原理图、指导手册、资源包
目录 前言 一.第一~十三届省/国赛真题 二.第八~十二届省/国赛客观题参考答案与解析 三.<"蓝桥杯"全国软件和信息技术专业人才大赛实训指导书> 四.<51单片 ...
- 2020蓝桥杯之单片机设计与开发(1)——CT107D开发板了解与准备
在快放假期间开始准备蓝桥杯,也感谢我的好朋友带我一起去了解这个比赛. 首先从单片机开始,比赛的时候使用的开发板是官方指定的CT107D单片机竞赛板V20. 先看上面的官方指定开发版原理图,包含了开发板 ...
- 蓝桥杯单片机学习过程记录(二十七)超声波模块
蓝桥杯单片机学习过程记录(二十七)超声波模块 超声波模块的学习,未验证. /* ------------------- 超声波模块 没模块未验证 2020.3.16 ----------------- ...
- 蓝桥杯单片机模块代码(AT24C02)(代码+注释)
本模块是上电可擦除EEPROM,用于存储需要的数据.与上一节使用的底层代码相同,运用同一个总线,其操做顺序与PCF8591除第二步几乎完全一样.相同部分具体可看:蓝桥杯单片机模块代码(PCF8591) ...
- **决战2021年单片机蓝桥杯笔记(1)**IIC PCF8591 AT24C02
**决战2021年单片机蓝桥杯笔记(1)**IIC PCF8591 AT24C02 I2C作为一种多用于板内同步串行通信方式,有一根SCL时钟线负责收发双方的时钟节拍,和一根SDA数据线负责传输数据, ...
- 基于STM32G431嵌入式竞赛板HAL库的程序设计——备赛蓝桥杯
LED的程序设计 8个LED在该嵌入式板子上分别与PC8-PC15相连,因为LED一段连接着VCC,高电平,所以低电平LED亮,高电平LED灭,LED的另一端连接着锁存器,而锁存器在高电平时不锁存数据 ...
- 递增三元组蓝桥杯c语言,蓝桥-递增三元组-蓝桥
蓝桥-递增三元组-蓝桥 蓝桥-递增三元组-蓝桥 手动求解一下会发现,B数组是关键 若固定b = B[i] a中的可能的取值是:a0 ----- at小于等于b的元素下标(小于b的个数) c中的可能取值 ...
最新文章
- plsql developer导出csv乱码问题
- Swift3.0语言教程获取C字符串
- 一个vue加egg.js的博客
- Zookeeper的一些Bugs
- 国内 RISC-V 产学研基地成立,Intel、Arm、RISC-V 将三分天下?
- 使用javaGUI编写检测是否有网
- 《计算机组网试验-DNS域名服务协议 》杭州电子科技大学
- 2021-12-11 根据单词首字母查找单词
- 【线性规划】投资的收益和风险
- DELL T7600工作站重新安装WIN7系统
- 1万元左右理财方法有那些
- 拼多多商品id怎么查看 拼多多店铺ID怎样看
- Android学习笔记之如何使用圆形菜单实现旋转效果...
- ARM架构SMMU驱动详解
- 处理textarea的空格和换行
- alin42490怎样解除_我们应该如何思维42490
- 习题5-6 对称轴 UVa1595
- Yapi测试插件--cross-request
- x3daudio1 7.dll怎么修复?修复方法推荐
- js sdk 一键分享 微信_微信JSSdk实现分享功能