蓝桥杯笔记
“免责声明” ( •̀ ω •́ )✧
代码未全部验证,也许存在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硬件概况
首先是国信长天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; //归0keypress
keyfree = 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; //归0keypress
keyfree = 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); //或者写给出的SlaveAddrW
IIC_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,即1T
T2L = 0xC7; //设定定时初值
T2H = 0xFE; //设定定时初值
AUXR |= 0x10; //启动定时器2
ES = 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就是*2
TH0 = 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();
}