二战蓝桥杯

没想到,还会再来一遍

勇敢梧桐树,不怕寒暑苦!

前作:stc15笔记

傻小子得学心眼

你知道我为什么去年只拿了个省三吗?

因为我的板子,有!错!误!这就导致代码在我的板子上面可以正常运行,但到了官方的板子上就跑不了。

Q:那你当时怎么不用比赛现场发的官方板子尝试一下?
A:太自信了,并且我的板子是Type-C接口有高级感。
Q:怎么想的?
A:失败有失败的原因,以史为鉴。
Q:只有这一种错误码?
A:我还出现了审题错误,误解题意导致某一项功能在最后的15分钟内去做更正;没找到客观题真题,导致只能根据仅存的知识乱蒙等。

今年的长进

  • 我从一些公众号上下载了往年真题资源(这是我以前有所不齿的);
  • 我弄到了一些往年真题的正解代码;
  • 我知道客观题是客观存在得了,不是传闻也不是CSDN上的某些文章所指的;
  • 有去年的笔记在,找大佬借了板子,研究起来少很多弯路。

比赛现场

硬件:比赛的时候,官方会给每个人下发开发板(提交后回收),你也可以用自己的。

软件:官方会发板上资源的数据手册,以及一些驱动代码。这些驱动代码中有的可能需要参赛选手自行补全,有的是完整的。以及其他一些内容。

考试时间是从上午考到过了饭点,因此参赛选手记得自己带点吃的,带足喝的。考试期间貌似允许有限制的出去上厕所。

自带

你可以带自己的板子去考试现场,而不用官方发的。不过,我就栽在了没用官方板子去做最终测试。

同样的,你也应该带一块万用表,用于测试ADC输出。你不需要携带任何形式的示波器,因为蓝桥杯还没有考过波形题,多是在开发板上实现某些功能逻辑。

当然,记得带干粮。

一点废话

你知道吗?去年我本来的企划是,当我考上了国赛(~“等我当上了国王,我要制定……”~),我要将这些整合在一起的博客,做得更好些,做到能拿出来给别人看。

一年后,只有“给别人看”做到了,看得人还是我。

新建工程

发现自己连STC的型号都忘记了。

礦 Vision上选择”STC MCU Database”,找“STC15F104E Series”,启用。

必备头文件

1
2
3
#include <intrins.h>
// #include <stdint.h> // 别想啦,根本没有这个库!
#include <STC15F104E.H>

别担心,STC15F104E.H不是你要敲的,而是右键insert的。

你也可以使用:

1
#include <reg51.h>

因为它们完全兼容!

接下来,将假装自己是个新手,重新入门蓝桥杯开发板,国赛冲冲冲!!!

板上资源浅析

显示

Q:这么多的板上资源,蓝桥杯开发板是怎么调用的?
A:3-8译码器。在原理图右侧,有一个74HC138,它的一侧连接着单片机的P25、P26、P27三个引脚;另一侧伸出来八个$Y_{n}$,不难发现,任何一个$Y_n$都在U25(74HC02)通过$Y_nC$连接着一个锁存器(CH573)
Q:这一堆是什么意思?
A:开发板实现了一个功能,P25、P26、P27三个引脚构成了三个比特位(三位二进制数),从0-7编码了板子上的几个基本模块。我们通过P25 P26 P27和其他的一些寄存器(例如P0)控制他们的读写和传输的数据。
Q:这七大模块是什么?他们与P25、P26、P27三个引脚构成的三位二进制数有什么对应关系?怎么输入我想要的信息到这些模块中去?
A:看表:

P25 P26 P27 模块 负责数据输入的STC寄存器
001(4) LED(共阳极) P0
101(5) 杂项 P0
011(6) 数码管数位选择 P0
111(7) 数码管 P0

注:P27才是对应二进制数中的高位;我们还要注意到,在U25(74HC02)上,WE引脚的电平控制着我们是否要通过P25-27来操作锁存器。WE的具体功能此处不做解释,你只需要知道你应当把它通过跳线帽置于高电平即可。

E²PROM

型号:AT24C02
通信方式:P20和P21的I2C,板上将A0、A1、A3直接接地,地址直接全填入0.

D/A、A/D

型号:PCF8591
通信方式:P20和P21的I2C,板上将A0、A1、A3直接接地,地址直接全填入0.

时钟芯片

型号:DS1302
通信方式:莫名其妙的IIC。反正会给驱动。

矩阵键盘

由P3的4、5号位、P3低四位和P42、P44控制。P3低四位扫描行,高四位扫描列;剩下的四个扫描行。

温度传感器

型号:DS18B20
通信方式:P14的单线通信。直接用驱动就可,可能会有少量内容需要我们去补全,应该对DS18B20的驱动写法熟悉。

驱动代码

注意!

我的建议是:一定要做到熟悉驱动代码,特别是底层通信(如onewire、I2C),防止官方哪天突然设下陷阱。当然了,我不确定!

注意!

  • int是16位的,建议先用int typedef出一个int16_t出来再用,防止自己混淆。
  • 函数内变量的声明/初始化应放在函数内最前方,不允许在代码的中间(或末尾)创建变量。(疑似汇编的坑)

方便一切的基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <intrins.h>
#include <reg51.h>
#define attach(x, y, z) P25=(x); P26=(y); P27=(z);
#define detach() P27=0; P25=0; P26=0; // 注意顺序,P27必须在前
#define write(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = (dat); detach(); // write基本只被led和数码管调用,所以应该是0xFF

typedef unsigned char uint8_t;
typedef unsigned int uint16_t; // 注意,int是16位的
typedef unsigned long uint32_t;

void Delay_1us() //@12.000MHz
{
_nop_();
_nop_();
}

void Delay_ns(uint8_t ns) {
while (ns -= 10) {
unsigned char data i;

i = 27;
while (--i);
}
}


void Delay_ms(uint8_t ms) { //@12.000MHz

while (ms--) {
unsigned char data i, j;

_nop_();
_nop_();
i = 12;
j = 168;
do {
while (--j);
} while (--i);
}
}

void Init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);
}

点灯大师

共阳极!!!

1
2
3
4
5
6
7
8
9
10
11
void LED_On(uint8_t index) {
attach(0, 0, 1);
P0 &= 0xFF ^ 1 << index;
detain();
}

void LED_Off(uint8_t index) {
attach(0, 0, 1);
P0 |= 1 << index;
detain();
}

改成宏

1
2
3
4
#define LED_On(index) led_statue &= 0xFF ^ (1 << (index) ); write(0, 0, 1, led_statue);
#define LED_Off(index) led_statue |= 1 << (index); write(0, 0, 1, led_statue);

uint8_t led_statue = 0xFF;

数码管

阅读开发手册

共阳数码管段码表.pdf,这是比赛中直接给出的从0-F的段码表。

代码

代码相当简单,直接宏,节省空间。

1
#define NT_ShowDat(pos,  dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (NT_code[dat]));

多位数你就直接用中断定时器吧,这里直接上代码,虽然后面还会再提。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
code uint8_t NT_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF};

#define NT_BLACK 16
#define NT_C 0x0C
#define NT_P 17
#define NT_A 0x0A
#define NT_H 19
#define NT_I 0x01

uint8_t NT_buffer[8] = {NT_BLACK, NT_BLACK, NT_BLACK, NT_BLACK, NT_BLACK, NT_BLACK, NT_BLACK, NT_BLACK};
uint8_t NT_index;

void Timer0_Init(void) //1000微秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x20; //设置定时初始值
TH0 = 0xD1; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
}

void Init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);
Timer1_Init();
EA = 1;
}

#define NT_Show(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (NT_code[(dat)]));
#define NT_ShowDot(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (NT_code[(dat)] & 0x7F));

void Timer0_Int() interrupt 1 { // 中断几都可以,但是延迟要保证>=1ms而<=10ms,快了有残影,满了人眼能识别,我选择1ms
NT_index %= 8;
if (NT_buffer[NT_index] & 0x80) { // 小数点注记方法:第八位置1
NT_ShowDot(NT_index, NT_buffer[NT_index] & 0x7F);
}
else {
NT_Show(NT_index, NT_buffer[NT_index]);
}
NT_index++;
}

这里我用定时器1,每当定时器被触发时,就显示一个新的未被显示过的数字(“更新”)。

如何确定每次更新时,要显示什么样的数字呢?我先在代码里保存好段码表(NT_code),并设置一个缓冲区(NT_buffer),缓冲区与数码管一一对应,里存储了一些与段码表相对应的下标。定时器代码从缓冲区(NT_buffer)获取下标,利用下标从段码表(NT_code)中获取段码,从而把相应的数字显示到NT_index所指示的数码管位置上。

简易的显示:(注意,显示方式与前作不同)

1
2
3
4
5
6
7
8
void NT_ShowNum(uint8_t pos, uint32_t dat) {
do {
NT_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (dat);
}

初始化部分:

1
2
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);

这是为了实现:

  • 取消对数码管的选择
  • 当任何数码管被选择时,它一定不显示任何内容(每一段LED都不亮)

注意!

数码管的LED共阳极,谁不显示谁置1

注意!

当你绘制题目中的界面时,你应当确保八个数码管都被你重新写入一遍。这是为了防止其他页面上的数据未被覆盖,导致切换到一个新的界面时,旧的数据还在上面显示。

用例

1
2
3
4
5
6
7
8
9
void main(void) { // 没错,void main()!!!
uint8_t index = 0;
Init();
while(1) {
NT_buffer[index % 8] = index | 0x80;
Delay_ms(1000);
index = ++index % 16;
}
}

矩阵按键

既然保证一次只有一个按钮被按下,那么我的思路是,将高四位和低四位的电平保存下来(P42和P44作为第七位和第八位),通过一个简单的编码规则判断按键编号。

简简单单,上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
uint8_t MatrixKey_Scan() {
uint8_t temp = 0x00;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x0C || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7D: return 5;
case 0xBD: return 6;
case 0xDD: return 7;
case 0xED: return 8;
case 0x7B: return 9;
case 0xBB: return 10;
case 0xDB: return 11;
case 0xEB: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

没有什么初始化代码。

编码的规律你可以通过Windows上自带的计算器看出来。(切换到程序员模式,然后打开二进制显示,可看出7、B、D、E这四个“四位数”都是3个1,1个0,并且0按一定次序“移动”)

这个编码规则是:从1-16,高四位:7-B-D-E,低四位:E-D-B-7(高四位反过来),高四位每走一个循环,低四位切换到下一个。

A/D与D/A

赛题中,IIC代码已经给出,你需要完成DS18B20的驱动部分。

阅读开发手册

PCF8591.pdf

资料

  • 地址:P5右上角,根据硬件原理图可知,地址为0x90,根据读写再去或1(读)或0(写)
  • I2C通信规则:P14
    • 写:PCF8591的写地址->控制字(P6)->数据(D/A输出量,或->结束
    • 读:PCF8591的读地址->PCF8591发送高八位->主机应答->PCF8591发送第八位->无应答,直接结束
  • A/D通道:共有4个,根据控制字(P6)来确定使用哪个
  • D/A通道:仅有1个,根据给出的公式$V_{AOUT}=V_{AGND}+\frac{V_{REF}-V_{AGND}}{256}\Sigma^{7}_{i=0}D_i\times2_i$这么一堆。根据我们小学学前班里学过的公式,它实际上想表达的是,从模拟地的电平(当做0就好)到参考电压(当做VCC就好,即5V)之间的256个阶跃。

代码

驱动部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void PCF_Write(uint8_t ctrl, uint8_t dat) {
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 控制字
IIC_WaitAck();
IIC_SendByte(dat); // D/A数据
IIC_WaitAck();
IIC_Stop();
}

float PCF_Read() {
uint8_t res;
// 先写入一次,这是因为PCF8591只会在写入之后做一次A/D转换,直接读取将读到上一次转换之后的值。
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(0x43); // 这里需要确定一下哪个通道要被读取,板子上只有ch2接入了变阻器,所以这里应该是0x43
IIC_WaitAck();
IIC_Stop();
// 读取数据
IIC_Start();
IIC_SendByte(0x91); // 读地址
IIC_WaitAck();
res = IIC_RecByte();// 读取1byte即可
IIC_SendAck(0);
IIC_Stop();
return res * 5.0f / 255.0f;
}

实际上这部分的IIC_WaitAck()IIC_SendAck(...)应该用if

初始化部分:

1
PCF_Write(0x40, 0x00);	// ain channels

注意!

  • 第一次读取到的值一定是0x80,也就是十进制的128,这个数据是为了后面如果要用到自动递增通道(P6,第三位),而给你一个起始值,让你在收到这个起始值后,你就能够知道当前数据开始从0通道开始了。
  • A/D转换周期总是在发送一个有效的读模式地址到PCF8591设备后开始。
    ——https://blog.csdn.net/qq_37429313/article/details/115285842

注意!

建议结合省赛题来看。

时间

阅读开发手册

DS1302.pdf

这就是所谓“掉电”之后仍能正常计时的计时芯片。当你的MCU不运行时,它还可以正常计时,只要它两端供电——因为它自己有一个晶振!

资料

  • 地址:你不需要关心
  • 通讯协议:你不需要关心
  • 寄存器地址:P9,一个很显眼的表格
  • 寄存器存储规则:
    • 对于分秒:它们通过三位二进制数表示分/秒的十进制下的十位数部分,通过低四位表示分/秒的十进制下的个位数部分,也就是借助了七位二进制数存储十进制的个位和十位数。例如:用“0x42”表达“42(dec)”、“0x10”表示“10(dec)”
    • 对于时:类似于分秒,不过,BIT7存储是12小时制还是24小时制;BIT4和BIT5一起存储小时的十位数部分(0/1/2),或者是通过BIT5确定现在是上午还是下午(早上0,晚上1),通过BIT4存储小时的十位数部分(0/1);通过低四位存储小时的个位数部分
    • 对于年月日:以此类推即可。
  • 寄存器细节讲解:
    • WP:写保护(Write Protect),在写入时间之前记得将WP置零
      • “The initial power-on state is not defined. Therefore, the WP bit should be cleared before attempting to write to the device.”
    • CH:时钟停止(Clock Halt),当它置一时有效,此时DS1302自带的晶振时钟信号终止,计时停止。开始计时时,记得将它置零。

代码

注意!

在官方给出的代码中,DS1302PCF8591的头文件里都定义了SCKSDA,需要我们手动重命名。当然,我推荐你:ctrl+H替换它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void TIME_Write(uint8_t* dat) {
int8_t i = 0;
uint8_t reg_addr = 0x80;
Write_Ds1302_Byte(0x8E, 0x00); // WP clear
for (i = 2; i >= 0; --i) {
Write_Ds1302_Byte(reg_addr, dat[i]);
reg_addr += 2;
}
Write_Ds1302_Byte(0x8E, 0x80); // WP set
}

void TIME_Read(uint8_t* dat) {
int8_t i;
uint8_t reg_addr = 0x81;
Write_Ds1302_Byte(0x8E, 0x80); // WP set, 比赛的时候可以不写
for (i = 2; i >= 0; --i) {
dat[i] = Read_Ds1302_Byte(addr);
addr += 2;
}
}

注意!

上面的代码我偷了懒,一次性将时分秒全部读出来,而不是单独读时分秒。

温度检测

阅读开发手册

DS18B20.pdf

万恶的单线通信,我搞不清的延时方式。

资料

  • 地址:你不需要关心
  • 通信协议:万恶的单线通信,你不需要关心
  • 指令:
    • Skip ROM:0xCC,简单来说,“跳过关于ROM的一些操作”,用于结合其他指令使用(指:Convert T、Read Power Supply)(P5下方、P11详细说明)
    • Convert T:启动温度数据转换的条件。发送一个Convert T信号(0x44)(P3下方长段落中下部分、P11详细说明)
    • Read Power Supply:0xB4,简单来说,用于读取数据,放在Skip ROM指令之后(P5下方、P11详细说明)
  • 返回数据:
    • 精度:0.5deg/4bits,也就是$2^{-4}=0.0625$。当然,它有0.5deg、0.25deg、0.125deg和0.0625deg四个可选精度,对应了不同的字节数(P3)
    • 范围:略
    • 负数的表示:最高位置一

代码

在官方驱动里,已经给出了Read_DS18B20()函数,他会直接帮我们读取结果,这样我们就不用担心自己实现一个驱动了。我们只需要解析读取到的结果即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
float TEMPER_Read() {
uint16_t res = 0;
uint8_t high, low;

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
Delay_OneWire(200);

low = Read_DS18B20();
high = Read_DS18B20();

res = high & 0x0F;
res = (res << 8) | low;

return res * 0.0625f;
}

初始化代码:

1
init_ds18b20();

E2PROM

阅读开发手册

AT24C02.pdf

资料

  • 地址:
    • 写地址:0xA0
    • 读地址:0xA1

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void E2PROM_Write(uint8_t addr, uint8_t* dat, uint8_t len) {
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while (len--) {
IIC_SendByte(dat[len]);
IIC_WaitAck();
}
IIC_Stop();
}

void E2PROM_Read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();

IIC_Start(); // Restart
IIC_SendByte(0xA1); // Read Address
IIC_WaitAck();
for (i = 0; i < len; ++i) {
dat[i] = IIC_RecByte();
IIC_SendAck(0);
}
}

超声波

常温常压下声速370m/s,基本原理是,通过P1^0发送、通过P1^1接收,利用计时器计中间的间隔时间,然后乘以0.017。下面的代码记住即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
sbit TX = P1^0;
sbit RX = P1^1;

void sonic_delay14us() //@12.000MHz
{
unsigned char i;

_nop_();
i = 4;
while (--i);
}


void sonic_send_signal() {
uint8_t i;
for (i = 0; i < 8; ++i) {
TX = 1;
sonic_delay14us();
TX = 0;
sonic_delay14us();
}
}

uint8_t sonic_get_signal() {
uint16_t tim; // 计数时间

// AUXR &= 0xFB;
// TMOD &= 0xF0;
TF0 = 0;
TH0 = 0x00; // 计数器初始化
TL0 = 0x00;
sonic_send_signal();
TR0 = 1; // 开始计数
while ( (RX == 1) && (TF1 == 0) ); // 接收到信号时,RX == 1;如果计数器溢出(TF==1)则结束计时。
TR0 = 0; // 计数结束
// 当正常接收到信号时
if (TF1 == 0) {
tim = TH0;
tim = (sonic_time << 8) | TL0;
return (uint8_t)(tim * 0.017); // distance = 0.000001s * 34000 cm/s / 2 = 0.017 cm/s
}
// 若溢出,中断标志清零,并返回0
TF0 = 0;
return 0;
}

中断和计时器资源浅析

如果忽然忘了一些内容(就像我去年考场上遇到的情况),可以去查一下手册,很快就能复健,毕竟比赛时间真的非常充裕。

注意!

  • 应该把数码管显示相关代码放在Timer0中
  • 执行时间较长的代码、自带delay的代码(例如TEMPER_Read())不要放在计时器中断里。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

void Timer0_Int() interrupt 1 {
NT_index %= 8;
if (NT_buffer[NT_index] & 0x80) { // 小数点注记方法:第八位置1
NT_ShowDot(NT_index, NT_buffer[NT_index] & 0x7F);
}
else {
NT_Show(NT_index, NT_buffer[NT_index]);
}
NT_index++;
}

void Timer1_Int() interrupt 3 {
static uint8_t Timer1_counter = 0;
Timer1_counter++;
if (Timer1_counter <= 100)
return;
Timer1_counter = 0;
}

计数器

阅读开发手册

J3上的P34和紧挨着的上面那个NET_SIG相短接即可开启计数。这是使用Timer0计数,你需要TMOD |= 0x04;,并且TL0 = 0xFF;TH0 = 0xFF;即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void Timer0_Init(void)
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x04;
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
}

void Timer0_Int() interrupt 1 {
NE_counter++;
}

void Timer1_Int() interrupt 3 {
static uint16_t Timer1_counter = 0;
Timer1_counter++;

NT_index %= 8;
if (NT_buffer[NT_index] & 0x80) { // 小数点注记方法:第八位置1
NT_ShowDot(NT_index, NT_buffer[NT_index] & 0x7F);
}
else {
NT_Show(NT_index, NT_buffer[NT_index]);
}
NT_index++;

if (Timer1_counter >= 1000) {
NE_count_ready = 1;
}
else {
return;
}

Timer1_counter = 0;
}

在main函数中:

1
2
3
4
5
if (NE_count_ready == 1) {
NT_ShowNum(5, NE_counter);
NE_counter = 0;
NE_count_ready = 0;
}

赛题

今年将比往年更广泛地涉猎题目!!!

实话说,写完驱动之后,我就不想去做这些题目了,单纯是因为懒癌犯了。

模板

当然,用模板的前提是要有能力改变它。

注意!

注意,模板代码与上面的有所调整,比如write里的P0=0变成了P0=0xFF等。

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
#include <intrins.h>
#include <STC15F104E.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

#define iic_start IIC_Start
#define iic_stop IIC_Stop
#define iic_send_byte IIC_SendByte
#define iic_send_ack IIC_SendAck
#define iic_rec_byte IIC_RecByte
#define iic_wait_ack IIC_WaitAck

#define temper_write Write_DS18B20
#define temper_read Read_DS18B20

////////////////////////////////////////////////////////////

#define attach(x, y, z) P25 = (x); P26 = (y); P27 = (z);
#define detach() P27 = 0; P25 = 0; P26 = 0;
#define write_led(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = dat; detach();

typedef char int8_t;
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

////////////////////////////////////////////////////////////

uint8_t timer_key;
uint8_t interface;

////////////////////////////////////////////////////////////

uint8_t led_statue 0xFF;

#define led_on(index) led_statue &= 0xFF ^ (1 << (index)); write_led(0, 0, 1, led_statue);
#define led_off(index) led_statue |= 1 << (index); write_led(0, 0, 1, led_statue);
#define led_invert(index) led_statue ^= 1 << (index); write_led(0, 0, 1, led_statue);

////////////////////////////////////////////////////////////

#define nt_blank 16
#define nt_interval 17 // 间隔符
#define nt_h 18
#define nt_p 19
#define nt_u 20
#define nt_l 21
#define nt_n 22
code uint8_t nt_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF, 0xBF, 0x89, 0x8C, 0xC1, 0xC7, 0xC8};
uint8_t nt_buf[] = [nt_blank, nt_blank, nt_blank, nt_blank, nt_blank, nt_blank, nt_blank, nt_blank];
uint8_t led_index;

#define nt_show(pos, dat) write_led(1, 0, 1, (1 << (pos))); write_led(1, 1, 1, dat);
#define nt_dot(pos, dat) write_led(1, 0, 1, (1 << (pos))); write_led(1, 1, 1, nt_code[dat] | 0x80);

////////////////////////////////////////////////////////////

void delay(uint16_t ms) //@12.000MHz
{
unsigned char data i, j;

while (ms--) {
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}

void delay_100us(void) //@12.000MHz
{
unsigned char data i, j;

i = 2;
j = 39;
do
{
while (--j);
} while (--i);
}

////////////////////////////////////////////////////////////

void nt_len(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buf[pos] = dat % 10;
dat /= 10;
pos--;
} while (len--);
}

void nt_len_dot(uint8_t pos, uint32_t dat, uint8_t len, uint8_t dot_pos) {
do {
if (pos == dot_pos) {
nt_buf[pos] = dat % 10 | 0x80;
}
else {
nt_buf[pos] = dat % 10;
}
dat /= 10;
pos--;
} while (len--);
}

void nt_blank(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buf[pos] = dat % 10;
dat /= 10;
pos--;
} while (len-- && dat);
for ( ; len; len--, pos--) {
nt_buf[pos] = nt_blank;
}
}

////////////////////////////////////////////////////////////

void pcf_write(uint8_t ctrl, uint8_t dat) {
iic_start();
iic_send_byte(0x90);
iic_wait_ack();
iic_send_byte(ctrl);
iic_wait_ack();
iic_send_byte(dat);
iic_wait_ack();
iic_stop();
}

float pcf_read(uint8_t ctrl) {
uint8_t temp;

iic_start();
iic_send_byte(0x90);
iic_wait_ack();
iic_send_byte(ctrl);
iic_wait_ack();

iic_start();
iic_send_byte(0x91);
iic_wait_ack();
temp = iic_rec_byte();
iic_send_ack(0);
iic_stop();
return temp * 5.0f / 256.0f;
}

////////////////////////////////////////////////////////////

#define ds1302 // etc

////////////////////////////////////////////////////////////

bit init_ds18b20(void) {
bit init_flag = 0;

DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
init_flag = D!;
Delay_OneWire(5);

return init_flag;
}

float temper_read() {
uint16_t res;
uint8_t high, low;

init_ds18b20();
temper_write(0xCC);
temper_write(0x44);
Delay_OneWire(200);

init_ds18b20();
temper_write(0xCC);
temper_write(0xBE);
Delay_OneWire(200);

low = temper_read();
high = temper_read();
res = high & 0x0F;
res <<= 8;
res |= low;
return res * 0.0625f;
}

////////////////////////////////////////////////////////////

void e2prom_write(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;

iic_start();
iic_send_byte(0xA0);
iic_wait_ack();
iic_send_byte(addr);
iic_wait_ack();
for(i = 0; i < len; ++i) {
iic_send_byte(dat[i]);
iic_wait_ack();
delay_100us();
}
iic_stop();
}

void e2prom_read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;

iic_start();
iic_send_byte(0xA0);
iic_wait_ack();
iic_send_byte(addr);
iic_wait_ack();

iic_start();
iic_send_byte(0xA1);
iic_wait_ack();
for(i = 0; i < len; ++i) {
dat[i] = iic_rec_byte();
iic_wait_ack();
delay(1);
}
iic_stop();
}

////////////////////////////////////////////////////////////

sbit TX = P1^0;
sbit RX = P1^1;

void sonic_init() {
// timer
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x00; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 0; //定时器0 don't 开始计时
}

void sonic_send() {
uint8_t i;

for(i = 0; i < 8; ++i) {
TX = 1;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
TX = 0;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
}
}

float sonic_read() {
uint16_t tim;

// sonic_init();
sonic_send();
TR0 = 1;
while ( (RX == 1) && (TF0 != 1));
TR0 = 0;
if (TF0 == 0) {
tim = TH0;
tim <<= 8;
tim |= TL0;
return (uint8_t)(tim / 2.0f * 34000.0f * 1e-6);
}
TF0 = 0;
return 0;
}

////////////////////////////////////////////////////////////

uint8_t keys_scan() {
uint8_t temp;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x30 || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7B: return 5;
case 0xBB: return 6;
case 0xDB: return 7;
case 0xEB: return 8;
case 0x7D: return 9;
case 0xBD: return 10;
case 0xDD: return 11;
case 0xED: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

////////////////////////////////////////////////////////////

// 变量

////////////////////////////////////////////////////////////

void draw(void) {

}

void key_event(uint8_t key) {
static bit press_flag = 0;

if (timer_key < 50) {
return;
}
timer_key = 0;

switch (key) {
case 13: // S4
if (press_flag)
return;
press_flag = 1;

interface++;
interface %= 3;

break;
case 9: // S5
if (press_flag)
return;
press_flag = 1;

break;
case 14: // S8
if (press_flag)
return;
press_flag = 1;

break;
case 10: // S9
if (press_flag)
return;
press_flag = 1;

break;
default:
press_flag = 0;
}
}

void update(void) {

}

////////////////////////////////////////////////////////////

void init(void) {

}

void main(void) {
init();
while (1) {
draw();
update();
key_event(keys_scan());
}
}

////////////////////////////////////////////////////////////

void timer0_int() interrupt 1 {

}

void timer1_int() interrupt 3 {
nt_index %= 8;
if (nt_buffer[nt_index] & 0x80) {
nt_dot(nt_index, nt_buf[nt_index] & 0x7F);
}
else {
nt_show(nt_index, nt_buf[nt_index]);
}
nt_index++;
timer_key++;
}

特别的

onewire.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include "onewire.h"
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/

//
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;
}

onewire.h

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __ONEWIRE_H
#define __ONEWIRE_H

#include <reg51.h>

sbit DQ = P1^4;

void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);

#endif

TH13

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
#include <intrins.h>
#include <STC15F104E.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

//////////////////////////////////////////////////////////////////////////////

#define attach(x, y, z) P25=(x); P26=(y); P27=(z);
#define detach() P27=0; P25=0; P26=0; // 注意顺序,P27必须在前
#define write(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = (dat); detach();

typedef unsigned char int8_t;
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

//////////////////////////////////////////////////////////////////////////////

uint8_t led_statue = 0xFF;

#define led_on(index) led_statue &= 0xFF ^ (1 << (index) ); write(0, 0, 1, led_statue);
#define led_off(index) led_statue |= 1 << (index); write(0, 0, 1, led_statue);
#define led_inv(index) led_statue ^= 1 << (index); write(0, 0, 1, led_statue); // 翻转

//////////////////////////////////////////////////////////////////////////////

uint16_t timer_S9;
uint16_t timer_temper_humid;
uint16_t timer_NE;
uint8_t timer_LED4;

//////////////////////////////////////////////////////////////////////////////

#define NT_BLANK 16
#define NT_INTERVAL 17 // 间隔符
#define NT_H 18
#define NT_P 19
#define NT_U 20
#define NT_L 21;

code uint8_t nt_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF, 0xBF, 0xF6, 0x8C, 0xC1, 0xC7};
uint8_t nt_buffer[8] = {NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK};
uint8_t nt_index;

#define nt_show(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)]));
#define nt_showdot(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)] & 0x7F));

//////////////////////////////////////////////////////////////////////////////

float volt;
float volt_para[2];
uint8_t volt_para_select;
uint8_t sonic_dist;
uint8_t sonic_started;

uint8_t timer_l8;
uint16_t timer_key;

//////////////////////////////////////////////////////////////////////////////

void delay_ns(uint8_t ns) { //@12.000MHz
while (ns -= 10) {
unsigned char data i;

i = 27;
while (--i);
}
}

void delay_ms(uint8_t ms) { //@12.000MHz

while (ms--) {
unsigned char data i, j;

_nop_();
_nop_();
i = 12;
j = 168;
do {
while (--j);
} while (--i);
}
}

////////////////////////////////////////////////////////////////////////////

void nt_show_num(uint8_t pos, uint16_t dat) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (dat);
}

void nt_show_num_len(uint8_t pos, uint16_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len);
}

////////////////////////////////////////////////////////////////////////////

void pcf_write(uint8_t dat) {
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(0x43); // 控制字
IIC_WaitAck();
IIC_SendByte(dat); // D/A数据
IIC_WaitAck();
IIC_Stop();
}

float pcf_read() {
uint8_t res;
// 先写入一次,这是因为pcf8591只会在写入之后做一次A/D转换,直接读取将读到上一次转换之后的值。
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(0x43); // 这里需要确定一下哪个通道要被读取,AIN1是光敏电阻的传感器
IIC_WaitAck();
IIC_Stop();
// 读取数据
IIC_Start();
IIC_SendByte(0x91); // 读地址
IIC_WaitAck();
res = IIC_RecByte();// 读取1byte即可
IIC_SendAck(0);
IIC_Stop();
return res * 5.0f / 255.0f;
}

////////////////////////////////////////////////////////////////////////////

void ds1302_write(uint8_t* dat) {
int8_t i = 0;
uint8_t reg_addr = 0x80;
Write_Ds1302_Byte(0x8E, 0x00); // WP clear
for (i = 2; i >= 0; --i) {
Write_Ds1302_Byte(reg_addr, dat[i]);
reg_addr += 2;
}
Write_Ds1302_Byte(0x8E, 0x80); // WP set
}

void ds1302_read(uint8_t* dat) {
int8_t i;
uint8_t reg_addr = 0x81;
Write_Ds1302_Byte(0x8E, 0x80); // WP set
for (i = 2; i >= 0; --i) {
dat[i] = Read_Ds1302_Byte(reg_addr);
reg_addr += 2;
}
}

////////////////////////////////////////////////////////////////////////////

float temper_read() {
uint16_t res = 0;
uint8_t high, low;

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
Delay_OneWire(200);

low = Read_DS18B20();
high = Read_DS18B20();

res = high & 0x0F;
res = (res << 8) | low;

return res * 0.0625f;
}

////////////////////////////////////////////////////////////////////////////

void e2prom_write(uint8_t addr, uint8_t* dat, uint8_t len) {
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while (len--) {
IIC_SendByte(dat[len]);
IIC_WaitAck();
}
IIC_Stop();
}

void e2prom_read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();

IIC_Start(); // Restart
IIC_SendByte(0xA1); // Read Address
IIC_WaitAck();
for (i = 0; i < len; ++i) {
dat[i] = IIC_RecByte();
IIC_SendAck(0);
}
}

////////////////////////////////////////////////////////////////////////////

sbit TX = P1^0;
sbit RX = P1^1;

void sonic_delay14us() //@12.000MHz
{
unsigned char i;

_nop_();
i = 4;
while (--i);
}


void sonic_send_signal() {
uint8_t i;
for (i = 0; i < 8; ++i) {
TX = 1;
sonic_delay14us();
TX = 0;
sonic_delay14us();
}
}

uint8_t sonic_get_dist() {
uint16_t tim; // 计数时间

// AUXR &= 0xFB;
// TMOD &= 0xF0;
TF0 = 0;
TH0 = 0x00; // 计数器初始化
TL0 = 0x00;
sonic_send_signal();
TR0 = 1; // 开始计数
while ( (RX == 1) && (TF1 == 0) ); // 接收到信号时,RX == 1;如果计数器溢出(TF==1)则结束计时。
TR0 = 0; // 计数结束
// 当正常接收到信号时
if (TF1 == 0) {
tim = TH0;
tim = (tim << 8) | TL0;
return (uint8_t)(tim * 0.017); // distance = 0.000001s * 34000 cm/s / 2 = 0.017 cm/s
}
// 若溢出,中断标志清零,并返回0
TF0 = 0;
return 0;
}

////////////////////////////////////////////////////////////////////////////

uint8_t keys_scan() {
uint8_t temp = 0x00;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x0C || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
delay_ms(10);
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7D: return 5;
case 0xBD: return 6;
case 0xDD: return 7;
case 0xED: return 8;
case 0x7B: return 9;
case 0xBB: return 10;
case 0xDB: return 11;
case 0xEB: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

////////////////////////////////////////////////////////////////////////////

void timer0_Init(void) //1000微秒12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x00; //设置定时初始值
TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
// ET0 = 1;
}

void timer1_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; // timer1中断
}

//////////////////////////////////////////////////////////////////////////////

void draw_volt() {
nt_buffer[0] = NT_U;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_buffer[3] = NT_BLANK;
nt_buffer[4] = NT_BLANK;
nt_show_num(7, volt * 100);
nt_buffer[5] |= 0x80;
}

void draw_para() {
nt_buffer[0] = NT_P;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_show_num_len(4, volt_para[0] * 10, 2);
nt_buffer[3] |= 0x80;
nt_buffer[5] = NT_BLANK;
nt_show_num_len(7, volt_para[1] * 10, 2);
nt_buffer[6] |= 0x80;
}

void draw_dist() {
nt_buffer[0] = NT_L;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_buffer[3] = NT_BLANK;
nt_buffer[4] = NT_BLANK;
if (sonic_started) {
nt_buffer[5] = NT_BLANK;
nt_show_num(7, sonic_dist);
nt_buffer[6] |= 0x80;
}
else {
nt_buffer[5] = 0x0A;
nt_buffer[6] = 0x0A;
nt_buffer[7] = 0x0A;
}
}

uint8_t interface = 0;

void draw(void) {
switch (interface) {
case 0: // 电压界面
draw_volt();
led_on(0); led_off(1); led_off(2);
break;
case 1: // 测量界面
draw_dist();
led_on(1); led_off(0); led_off(2);
break;
case 2: // 参数界面
draw_para();
led_on(2); led_off(0); led_off(1);
break;
}
}

void key_event(uint8_t key) {
switch (key) {
case 13: // 界面按键
if (++interface > 2) {
interface = 0;
}
break;
case 9: // 参数选择
if (interface == 2) {
volt_para_select = !volt_para_select;
}
break;
case 5: // 加
if (interface == 2) {
volt_para[volt_para_select] += 0.5;
if (volt_para[volt_para_select] > 5.0) {
volt_para[volt_para_select] = 0.5;
}
}
break;
case 1: // 减
if (interface == 2) {
volt_para[volt_para_select] -= 0.5;
if (volt_para[volt_para_select] < 0.5) {
volt_para[volt_para_select] = 5.0;
}
}
break;
}
}

void update(void) {
volt = pcf_read();
if (volt >= volt_para[1] && volt <= volt_para[0]) {
sonic_started = 1;
}
else {
sonic_started = 0;
}

if (sonic_started) {
sonic_dist = sonic_get_dist();
if (sonic_dist < 20) {
pcf_write(51);
}
else if (sonic_dist >= 80) {
pcf_write(255);
}
else {
pcf_write((sonic_dist * 0.0667 - 0.335) * 0.0195);
}
}
else {
pcf_write(0);
}
}

//////////////////////////////////////////////////////////////////////////////

void init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);

init_ds18b20();

timer0_Init();
timer1_Init();

pcf_write(0x00); // ain channels

volt_para[0] = 4.5;
volt_para[1] = 0.5;

EA = 1;
}

void main(void) {
init();
while(1) {
draw();

if (timer_key >= 200) {
timer_key = 0;
key_event(keys_scan());
}

update();
}
}

////////////////////////////////////////////////////////////////////////////

void timer1_Int() interrupt 3 {
nt_index %= 8;
if (nt_buffer[nt_index] & 0x80) { // 小数点注记方法:第八位置1
nt_showdot(nt_index, nt_buffer[nt_index] & 0x7F);
}
else {
nt_show(nt_index, nt_buffer[nt_index]);
}
nt_index++;

if (sonic_started) {
timer_l8++;
if (timer_l8 >= 100) {
led_inv(7);
timer_l8 = 0;
}
}
else {
timer_l8 = 0;
}

++timer_key;
}

TH12

完整代码

写完之后,我就不知道它是怎么实现的了……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
#include <intrins.h>
#include <STC15F104E.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

//////////////////////////////////////////////////////////////////////////////

#define attach(x, y, z) P25=(x); P26=(y); P27=(z);
#define detach() P27=0; P25=0; P26=0; // 注意顺序,P27必须在前
#define write(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = (dat); detach();

typedef unsigned char int8_t;
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

//////////////////////////////////////////////////////////////////////////////

uint8_t led_statue = 0xFF;

#define led_on(index) led_statue &= 0xFF ^ (1 << (index) ); write(0, 0, 1, led_statue);
#define led_off(index) led_statue |= 1 << (index); write(0, 0, 1, led_statue);

//////////////////////////////////////////////////////////////////////////////

#define NT_BLANK 16
#define NT_INTERVAL 17 // 间隔符
#define NT_H 18
#define NT_P 19
#define NT_U 20
#define NT_L 21
#define NT_N 22

code uint8_t nt_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF, 0xBF, 0xF6, 0x8C, 0xC1, 0xC7, 0xC8};
uint8_t nt_buffer[8] = {NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK};
uint8_t nt_index;

#define nt_show(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)]));
#define nt_showdot(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)] & 0x7F));

//////////////////////////////////////////////////////////////////////////////

uint16_t freq;
uint16_t freq_save; // 频率缓存数据
uint16_t freq_period;
uint16_t freq_counter;

float volt;
float volt_ch3; // ch3实时数据
float volt_save; // 缓存电压数据
uint8_t volt_ch;

uint16_t timer_key;
uint16_t timer_freq;
uint16_t timer_s7;

bit led_enable;

//////////////////////////////////////////////////////////////////////////////

void delay_ns(uint8_t ns) { //@12.000MHz
while (ns -= 10) {
unsigned char data i;

i = 27;
while (--i);
}
}

void delay_ms(uint8_t ms) { //@12.000MHz

while (ms--) {
unsigned char data i, j;

_nop_();
_nop_();
i = 12;
j = 168;
do {
while (--j);
} while (--i);
}
}

////////////////////////////////////////////////////////////////////////////

void nt_shownum_len(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_dot(uint8_t pos, uint32_t dat, uint8_t len, uint8_t dot_pos) {
do {
if (pos == dot_pos) {
nt_buffer[pos] = (dat % 10) | 0x80;
}
else {
nt_buffer[pos] = dat % 10;
}
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_blank(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len && dat);
while (len--) {
nt_buffer[pos] = NT_BLANK;
pos--;
}
}

////////////////////////////////////////////////////////////////////////////

void pcf_write(uint8_t ctrl, uint8_t dat) {
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 控制字
IIC_WaitAck();
IIC_SendByte(dat); // D/A数据
IIC_WaitAck();
IIC_Stop();
}

float pcf_read(uint8_t ctrl) {
uint8_t res;
// 先写入一次,这是因为pcf8591只会在写入之后做一次A/D转换,直接读取将读到上一次转换之后的值。
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 这里需要确定一下哪个通道要被读取,AIN1是光敏电阻的传感器
IIC_WaitAck();
IIC_Stop();
// 读取数据
IIC_Start();
IIC_SendByte(0x91); // 读地址
IIC_WaitAck();
res = IIC_RecByte();// 读取1byte即可
IIC_SendAck(0);
IIC_Stop();
return res * 5.0f / 255.0f;
}

////////////////////////////////////////////////////////////////////////////

void ds1302_write(uint8_t* dat) {
int8_t i = 0;
uint8_t reg_addr = 0x80;
Write_Ds1302_Byte(0x8E, 0x00); // WP clear
for (i = 2; i >= 0; --i) {
Write_Ds1302_Byte(reg_addr, dat[i]);
reg_addr += 2;
}
Write_Ds1302_Byte(0x8E, 0x80); // WP set
}

void ds1302_read(uint8_t* dat) {
int8_t i;
uint8_t reg_addr = 0x81;
Write_Ds1302_Byte(0x8E, 0x80); // WP set
for (i = 2; i >= 0; --i) {
dat[i] = Read_Ds1302_Byte(reg_addr);
reg_addr += 2;
}
}

////////////////////////////////////////////////////////////////////////////

float temper_read() {
uint16_t res = 0;
uint8_t high, low;

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
Delay_OneWire(200);

low = Read_DS18B20();
high = Read_DS18B20();

res = high & 0x0F;
res = (res << 8) | low;

return res * 0.0625f;
}

////////////////////////////////////////////////////////////////////////////

void e2prom_write(uint8_t addr, uint8_t* dat, uint8_t len) {
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while (len--) {
IIC_SendByte(dat[len]);
IIC_WaitAck();
}
IIC_Stop();
}

void e2prom_read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();

IIC_Start(); // Restart
IIC_SendByte(0xA1); // Read Address
IIC_WaitAck();
for (i = 0; i < len; ++i) {
dat[i] = IIC_RecByte();
IIC_SendAck(0);
}
}

////////////////////////////////////////////////////////////////////////////

sbit TX = P1^0;
sbit RX = P1^1;

void sonic_send_signal() {
uint8_t i;
for (i = 0; i < 8; ++i) {
TX = 1;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
TX = 0;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
}

uint8_t sonic_get_dist() {
uint16_t tim; // 计数时间

// AUXR &= 0xFB;
// TMOD &= 0xF0;
TF0 = 0;
TH0 = 0x00; // 计数器初始化
TL0 = 0x00;
sonic_send_signal();
TR0 = 1; // 开始计数
while ( (RX == 1) && (TF1 == 0) ); // 接收到信号时,RX == 1;如果计数器溢出(TF==1)则结束计时。
TR0 = 0; // 计数结束
// 当正常接收到信号时
if (TF1 == 0) {
tim = TH0;
tim = (tim << 8) | TL0;
return (uint8_t)(tim * 0.017); // distance = 0.000001s * 34000 cm/s / 2 = 0.017 cm/s
}
// 若溢出,中断标志清零,并返回0
TF0 = 0;
return 0;
}

////////////////////////////////////////////////////////////////////////////

uint8_t keys_scan() {
uint8_t temp = 0x00;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x0C || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
delay_ms(10);
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7D: return 5;
case 0xBD: return 6;
case 0xDD: return 7;
case 0xED: return 8;
case 0x7B: return 9;
case 0xBB: return 10;
case 0xDB: return 11;
case 0xEB: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

////////////////////////////////////////////////////////////////////////////

void timer0_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x04;
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
}

void timer1_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
}

//////////////////////////////////////////////////////////////////////////////

uint8_t interface;

void draw_freq() {
nt_buffer[0] = 0x0F;
nt_shownum_blank(7, freq, 7);
if (led_enable) {
led_on(2); led_off(3); led_off(4);
}
else {
led_off(2); led_off(3); led_off(4);
}
}

void draw_period() {
nt_buffer[0] = NT_N;
nt_shownum_blank(7, freq_period, 7);
if (led_enable) {
led_on(3); led_off(2); led_off(4);
}
else {
led_off(2); led_off(3); led_off(4);
}
}

void draw_volt() {
nt_buffer[0] = NT_U;
nt_buffer[1] = NT_INTERVAL;
nt_shownum_len(2, volt_ch, 1); // 显示通道编号
nt_buffer[3] = NT_BLANK;
nt_buffer[4] = NT_BLANK;
nt_shownum_dot(7, volt * 100, 3, 5);
if (led_enable) {
led_on(4); led_off(2); led_off(3);
}
else {
led_off(2); led_off(3); led_off(4);
}
}

void draw() {
switch (interface) {
case 0:
draw_freq();
break;
case 1:
draw_period();
break;
case 2:
draw_volt();
break;
}

if (led_enable) {
if (volt_ch3 > volt_save) {
led_on(0);
}
else {
led_off(0);
}

if (freq > freq_save) {
led_on(1);
}
else {
led_off(1);
}
}
else {
led_statue = 0xFF;
led_off(0); // 懒得分离一个led_update()出来
}
}

void key_event(uint8_t key) {
static bit press_flag = 0; // 用于判断按键是不是刚刚按下——实现一些按键的下降沿触发

switch (key) {
case 13: // S4
if (timer_key < 100) { // 延时消抖
return;
}
timer_key = 0; // 按键延时计时器置0

if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1; // 实话说,这种写法是感觉告诉我的,,,一坨史,,,它将在default中被置为0,,,感觉是朴素的小学数学思想所致

++interface;
if (interface >= 3) {
interface = 0;
}
if (interface == 2) {
volt_ch = 1;
}
break;
case 9: // S5
if (timer_key < 100) {
return;
}
timer_key = 0;

if (press_flag)
return;
press_flag = 1;

if (interface == 2) {
volt_ch = volt_ch == 1 ? 3 : 1;
}
break;
case 5: // S6
if (timer_key < 100) {
return;
}
timer_key = 0;

if (press_flag)
return;
press_flag = 1;

volt_save = volt_ch3;
break;
case 1: // S7
if (timer_s7 == 0) {
timer_s7++;
freq_save = freq;
}
if (timer_s7 >= 1000 && timer_s7 <= 1300) { // 这里是取个巧,配合外面的timer_s7=0,省时省力实现S7长按后的上升沿触发。1300是随便写的,防止触发不到,实际上1100就可以,写得大了心里舒坦。
led_enable = ~led_enable;
timer_s7 = 2000;
}
break;
default:
press_flag = 0;
break;
}
timer_s7 = 0;
}

void update() {
volt_ch3 = pcf_read(0x43);

if (volt_ch == 3) {
volt = volt_ch3;
}
else {
volt = pcf_read(0x41);
}
}

//////////////////////////////////////////////////////////////////////////////

void init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);

init_ds18b20();

timer0_Init();
timer1_Init();

pcf_write(0x41, 0x00); // ain channels
pcf_write(0x43, 0x00); // ain channels

volt_ch = 1;
led_enable = 1;

EA = 1;
}

void main(void) {
init();
while(1) {
draw();
update();
key_event(keys_scan());
}
}

////////////////////////////////////////////////////////////////////////////

void timer0_Int() interrupt 1 {
freq_counter++;
}

void timer1_Int() interrupt 3 {
nt_index %= 8;
if (nt_buffer[nt_index] & 0x80) { // 小数点注记方法:第八位置1
nt_showdot(nt_index, nt_buffer[nt_index] & 0x7F);
}
else {
nt_show(nt_index, nt_buffer[nt_index]);
}
nt_index++;

timer_key++;
timer_freq++;

if (timer_freq == 100) {
freq = freq_counter;
freq_period = 100000 / freq_counter;
freq_counter = 0;
timer_freq = 0;
}

if (timer_s7 > 0) {
timer_s7++;
}
}

TH11

关于开启和关闭timer(不是硬件时钟,而是软件时钟)我比较喜欢的一种做法:当timer==0时,认为timer关闭;当timer>0时,认为timer开启。

完整代码

还算简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
#include <intrins.h>
#include <STC15F104E.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

//////////////////////////////////////////////////////////////////////////////

#define attach(x, y, z) P25=(x); P26=(y); P27=(z);
#define detach() P27=0; P25=0; P26=0; // 注意顺序,P27必须在前
#define write(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = (dat); detach();

typedef unsigned char int8_t;
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

//////////////////////////////////////////////////////////////////////////////

uint8_t timer_key;

//////////////////////////////////////////////////////////////////////////////

uint8_t led_statue = 0xFF;

#define led_on(index) led_statue &= 0xFF ^ (1 << (index) ); write(0, 0, 1, led_statue);
#define led_off(index) led_statue |= 1 << (index); write(0, 0, 1, led_statue);

//////////////////////////////////////////////////////////////////////////////

#define NT_BLANK 16
#define NT_INTERVAL 17 // 间隔符
#define NT_H 18
#define NT_P 19
#define NT_U 20
#define NT_L 21
#define NT_N 22

code uint8_t nt_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF, 0xBF, 0xF6, 0x8C, 0xC1, 0xC7, 0xC8};
uint8_t nt_buffer[8] = {NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK};
uint8_t nt_index;

#define nt_show(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)]));
#define nt_showdot(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)] & 0x7F));

//////////////////////////////////////////////////////////////////////////////

float volt;
float param;

uint16_t counter;

uint16_t l3_counter;

uint32_t timer_l1;

bit e2prom_task;

//////////////////////////////////////////////////////////////////////////////

void delay_ns(uint8_t ns) { //@12.000MHz
while (ns -= 10) {
unsigned char data i;

i = 27;
while (--i);
}
}

void delay_ms(uint8_t ms) { //@12.000MHz

while (ms--) {
unsigned char data i, j;

_nop_();
_nop_();
i = 12;
j = 168;
do {
while (--j);
} while (--i);
}
}

////////////////////////////////////////////////////////////////////////////

void nt_shownum_len(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_dot(uint8_t pos, uint32_t dat, uint8_t len, uint8_t dot_pos) {
do {
if (pos == dot_pos) {
nt_buffer[pos] = (dat % 10) | 0x80;
}
else {
nt_buffer[pos] = dat % 10;
}
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_blank(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len && dat);
while (len--) {
nt_buffer[pos] = NT_BLANK;
pos--;
}
}

////////////////////////////////////////////////////////////////////////////

void pcf_write(uint8_t ctrl, uint8_t dat) {
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 控制字
IIC_WaitAck();
IIC_SendByte(dat); // D/A数据
IIC_WaitAck();
IIC_Stop();
}

float pcf_read(uint8_t ctrl) {
uint8_t res;
// 先写入一次,这是因为pcf8591只会在写入之后做一次A/D转换,直接读取将读到上一次转换之后的值。
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 这里需要确定一下哪个通道要被读取,AIN1是光敏电阻的传感器
IIC_WaitAck();
IIC_Stop();
// 读取数据
IIC_Start();
IIC_SendByte(0x91); // 读地址
IIC_WaitAck();
res = IIC_RecByte();// 读取1byte即可
IIC_SendAck(0);
IIC_Stop();
return res * 5.0f / 255.0f;
}

////////////////////////////////////////////////////////////////////////////

void ds1302_write(uint8_t* dat) {
int8_t i = 0;
uint8_t reg_addr = 0x80;
Write_Ds1302_Byte(0x8E, 0x00); // WP clear
for (i = 2; i >= 0; --i) {
Write_Ds1302_Byte(reg_addr, dat[i]);
reg_addr += 2;
}
Write_Ds1302_Byte(0x8E, 0x80); // WP set
}

void ds1302_read(uint8_t* dat) {
int8_t i;
uint8_t reg_addr = 0x81;
Write_Ds1302_Byte(0x8E, 0x80); // WP set
for (i = 2; i >= 0; --i) {
dat[i] = Read_Ds1302_Byte(reg_addr);
reg_addr += 2;
}
}

////////////////////////////////////////////////////////////////////////////

float temper_read() {
uint16_t res = 0;
uint8_t high, low;

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
Delay_OneWire(200);

low = Read_DS18B20();
high = Read_DS18B20();

res = high & 0x0F;
res = (res << 8) | low;

return res * 0.0625f;
}

////////////////////////////////////////////////////////////////////////////

void e2prom_write(uint8_t addr, uint8_t* dat, uint8_t len) {
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while (len--) {
IIC_SendByte(dat[len]);
IIC_WaitAck();
}
IIC_Stop();
}

void e2prom_read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();

IIC_Start(); // Restart
IIC_SendByte(0xA1); // Read Address
IIC_WaitAck();
for (i = 0; i < len; ++i) {
dat[i] = IIC_RecByte();
IIC_SendAck(0);
}
}

////////////////////////////////////////////////////////////////////////////

sbit TX = P1^0;
sbit RX = P1^1;

void sonic_send_signal() {
uint8_t i;
for (i = 0; i < 8; ++i) {
TX = 1;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
TX = 0;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
}

uint8_t sonic_get_dist() {
uint16_t tim; // 计数时间

// AUXR &= 0xFB;
// TMOD &= 0xF0;
TF0 = 0;
TH0 = 0x00; // 计数器初始化
TL0 = 0x00;
sonic_send_signal();
TR0 = 1; // 开始计数
while ( (RX == 1) && (TF1 == 0) ); // 接收到信号时,RX == 1;如果计数器溢出(TF==1)则结束计时。
TR0 = 0; // 计数结束
// 当正常接收到信号时
if (TF1 == 0) {
tim = TH0;
tim = (tim << 8) | TL0;
return (uint8_t)(tim * 0.017); // distance = 0.000001s * 34000 cm/s / 2 = 0.017 cm/s
}
// 若溢出,中断标志清零,并返回0
TF0 = 0;
return 0;
}

////////////////////////////////////////////////////////////////////////////

uint8_t keys_scan() {
uint8_t temp = 0x00;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x0C || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
delay_ms(10);
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7D: return 5;
case 0xBD: return 6;
case 0xDD: return 7;
case 0xED: return 8;
case 0x7B: return 9;
case 0xBB: return 10;
case 0xDB: return 11;
case 0xEB: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

////////////////////////////////////////////////////////////////////////////

void timer0_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x04;
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
// ET0 = 1;
}

void timer1_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
}

//////////////////////////////////////////////////////////////////////////////

uint8_t interface;

void draw_data() {
nt_buffer[0] = NT_U;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_buffer[3] = NT_BLANK;
nt_buffer[4] = NT_BLANK;
nt_shownum_dot(7, volt * 100, 3, 5);
}

void draw_param() {
nt_buffer[0] = NT_P;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_buffer[3] = NT_BLANK;
nt_buffer[4] = NT_BLANK;
nt_shownum_dot(7, param * 100, 3, 5);
}

void draw_counter() {
nt_buffer[0] = NT_N;
nt_shownum_blank(7, counter, 7);
}

void draw() {
switch (interface) {
case 0:
draw_data();
break;
case 1:
draw_param();
break;
case 2:
draw_counter();
break;
}

if (timer_l1 > 5000) {
led_on(0);
}
else {
led_off(0);
}

if (counter & 1) {
led_on(1);
}
else {
led_off(1);
}

if (l3_counter >= 3) {
led_on(2);
}
else {
led_off(2);
}
}

void key_event(uint8_t key) {
static bit press_flag = 0; // 用于判断按键是不是刚刚按下——实现一些按键的下降沿触发
if (timer_key < 50) {
return;
}
timer_key = 0;

switch (key) {
case 15: // S12
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1; // 实话说,这种写法是感觉告诉我的,,,一坨史,,,它将在default中被置为0,,,感觉是朴素的小学数学思想所致

l3_counter = 0;

++interface;
if (interface >= 3) {
interface = 0;
}

if (interface == 2) {
e2prom_task = 1;
}

break;

case 11: // S13
if (press_flag)
return;
press_flag = 1;

l3_counter = 0;

if (interface == 2) {
counter = 0;
}
else {
l3_counter++;
}


break;
case 16: // S16
if (press_flag)
return;
press_flag = 1;

l3_counter = 0;

if (interface == 1) {
param += 0.5;
if (param > 5.0) {
param = 0.0;
}
}
else {
l3_counter++;
}

break;

case 12: // S17
if (press_flag)
return;
press_flag = 1;

l3_counter = 0;

if (interface == 1) {
param -= 0.5;
if (param < 0) {
param = 5.0;
}
}
else {
l3_counter++;
}

break;

default:
press_flag = 0;
break;
}
}

bit volt_dat_judge() {
static bit is_lower = 0;
if (volt < param && !is_lower) {
is_lower = 1;
return 1;
}
else if (volt > param) {
is_lower = 0;
}
return 0;
}

uint8_t e2prom_buf = 0;

void update() {

volt = pcf_read(0x43);

if (volt < param && timer_l1 == 0) {
timer_l1++;
}
else if (volt >= param) {
timer_l1 = 0;
}

if (e2prom_task) {
e2prom_task = 0;
e2prom_buf = param * 10;
e2prom_write(0x00, &e2prom_buf, 1);
}

if (volt_dat_judge()) {
counter++;
}
}

//////////////////////////////////////////////////////////////////////////////

void init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);

init_ds18b20();

timer0_Init();
timer1_Init();

pcf_write(0x43, 0x00);

e2prom_read(0x00, &e2prom_buf, 1);
param = (float)e2prom_buf / 10.0;

EA = 1;
}

void main(void) {
init();
while(1) {
draw();
update();
key_event(keys_scan());
}
}

////////////////////////////////////////////////////////////////////////////

void timer1_Int() interrupt 3 {
nt_index %= 8;
if (nt_buffer[nt_index] & 0x80) { // 小数点注记方法:第八位置1
nt_showdot(nt_index, nt_buffer[nt_index] & 0x7F);
}
else {
nt_show(nt_index, nt_buffer[nt_index]);
}
nt_index++;

timer_key++;

if (timer_l1 > 0) {
timer_l1++;
}
}

TH10

遇挫:忘记外部源计数配置方法(TMOD |= 0x04;)导致timer0无法对外部脉冲计数,导致主程序和时钟都卡住了。

目前速度:一天切一题

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
#include <intrins.h>
#include <STC15F104E.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

//////////////////////////////////////////////////////////////////////////////

#define attach(x, y, z) P25=(x); P26=(y); P27=(z);
#define detach() P27=0; P25=0; P26=0; // 注意顺序,P27必须在前
#define write(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = (dat); detach();

typedef unsigned char int8_t;
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

//////////////////////////////////////////////////////////////////////////////

uint8_t timer_key;

//////////////////////////////////////////////////////////////////////////////

uint8_t led_statue = 0xFF;

#define led_on(index) led_statue &= 0xFF ^ (1 << (index) ); write(0, 0, 1, led_statue);
#define led_off(index) led_statue |= 1 << (index); write(0, 0, 1, led_statue);

//////////////////////////////////////////////////////////////////////////////

#define NT_BLANK 16
#define NT_INTERVAL 17 // 间隔符
#define NT_H 18
#define NT_P 19
#define NT_U 20
#define NT_L 21
#define NT_N 22

code uint8_t nt_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF, 0xBF, 0xF6, 0x8C, 0xC1, 0xC7, 0xC8};
uint8_t nt_buffer[8] = {NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK};
uint8_t nt_index;

#define nt_show(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)]));
#define nt_showdot(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)] & 0x7F));

//////////////////////////////////////////////////////////////////////////////

uint32_t freq_counter;
uint32_t freq;
uint16_t freq_timer;

float volt;
bit volt_use_rb2;

bit led_enable;

bit nt_enable;

//////////////////////////////////////////////////////////////////////////////

void delay_ns(uint8_t ns) { //@12.000MHz
while (ns -= 10) {
unsigned char data i;

i = 27;
while (--i);
}
}

void delay_ms(uint8_t ms) { //@12.000MHz

while (ms--) {
unsigned char data i, j;

_nop_();
_nop_();
i = 12;
j = 168;
do {
while (--j);
} while (--i);
}
}

////////////////////////////////////////////////////////////////////////////

void nt_shownum_len(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_dot(uint8_t pos, uint32_t dat, uint8_t len, uint8_t dot_pos) {
do {
if (pos == dot_pos) {
nt_buffer[pos] = (dat % 10) | 0x80;
}
else {
nt_buffer[pos] = dat % 10;
}
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_blank(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len && dat);
while (len--) {
nt_buffer[pos] = NT_BLANK;
pos--;
}
}

////////////////////////////////////////////////////////////////////////////

void pcf_write(uint8_t ctrl, uint8_t dat) {
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 控制字
IIC_WaitAck();
IIC_SendByte(dat); // D/A数据
IIC_WaitAck();
IIC_Stop();
}

float pcf_read(uint8_t ctrl) {
uint8_t res;
// 先写入一次,这是因为pcf8591只会在写入之后做一次A/D转换,直接读取将读到上一次转换之后的值。
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 这里需要确定一下哪个通道要被读取,AIN1是光敏电阻的传感器
IIC_WaitAck();
IIC_Stop();
// 读取数据
IIC_Start();
IIC_SendByte(0x91); // 读地址
IIC_WaitAck();
res = IIC_RecByte();// 读取1byte即可
IIC_SendAck(0);
IIC_Stop();
return res * 5.0f / 255.0f;
}

////////////////////////////////////////////////////////////////////////////

void ds1302_write(uint8_t* dat) {
int8_t i = 0;
uint8_t reg_addr = 0x80;
Write_Ds1302_Byte(0x8E, 0x00); // WP clear
for (i = 2; i >= 0; --i) {
Write_Ds1302_Byte(reg_addr, dat[i]);
reg_addr += 2;
}
Write_Ds1302_Byte(0x8E, 0x80); // WP set
}

void ds1302_read(uint8_t* dat) {
int8_t i;
uint8_t reg_addr = 0x81;
Write_Ds1302_Byte(0x8E, 0x80); // WP set
for (i = 2; i >= 0; --i) {
dat[i] = Read_Ds1302_Byte(reg_addr);
reg_addr += 2;
}
}

////////////////////////////////////////////////////////////////////////////

float temper_read() {
uint16_t res = 0;
uint8_t high, low;

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
Delay_OneWire(200);

low = Read_DS18B20();
high = Read_DS18B20();

res = high & 0x0F;
res = (res << 8) | low;

return res * 0.0625f;
}

////////////////////////////////////////////////////////////////////////////

void e2prom_write(uint8_t addr, uint8_t* dat, uint8_t len) {
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while (len--) {
IIC_SendByte(dat[len]);
IIC_WaitAck();
}
IIC_Stop();
}

void e2prom_read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();

IIC_Start(); // Restart
IIC_SendByte(0xA1); // Read Address
IIC_WaitAck();
for (i = 0; i < len; ++i) {
dat[i] = IIC_RecByte();
IIC_SendAck(0);
}
}

////////////////////////////////////////////////////////////////////////////

sbit TX = P1^0;
sbit RX = P1^1;

void sonic_send_signal() {
uint8_t i;
for (i = 0; i < 8; ++i) {
TX = 1;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); // 8个 _nop_();
TX = 0;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
}

uint8_t sonic_get_dist() {
uint16_t tim; // 计数时间

// AUXR &= 0xFB;
// TMOD &= 0xF0;
TF0 = 0;
TH0 = 0x00; // 计数器初始化
TL0 = 0x00;
sonic_send_signal();
TR0 = 1; // 开始计数
while ( (RX == 1) && (TF1 == 0) ); // 接收到信号时,RX == 1;如果计数器溢出(TF==1)则结束计时。
TR0 = 0; // 计数结束
// 当正常接收到信号时
if (TF1 == 0) {
tim = TH0;
tim = (tim << 8) | TL0;
return (uint8_t)(tim * 0.017); // distance = 0.000001s * 34000 cm/s / 2 = 0.017 cm/s
}
// 若溢出,中断标志清零,并返回0
TF0 = 0;
return 0;
}

////////////////////////////////////////////////////////////////////////////

uint8_t keys_scan() {
uint8_t temp = 0x00;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x0C || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
// delay_ms(10);
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7D: return 5;
case 0xBD: return 6;
case 0xDD: return 7;
case 0xED: return 8;
case 0x7B: return 9;
case 0xBB: return 10;
case 0xDB: return 11;
case 0xEB: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

////////////////////////////////////////////////////////////////////////////

void timer0_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x04;
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
}

void timer1_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}

//////////////////////////////////////////////////////////////////////////////

uint8_t interface;

void draw_freq() {
nt_buffer[0] = 0x0F;
nt_buffer[1] = NT_BLANK;
nt_shownum_blank(7, freq, 6);
}

void draw_volt() {
nt_buffer[0] = NT_U;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_buffer[3] = NT_BLANK;
nt_buffer[4] = NT_BLANK;
nt_shownum_dot(7, volt * 100, 3, 5);
}

void draw() {
switch (interface) {
case 0:
draw_freq();
break;
case 1:
draw_volt();
break;
default:
interface = 0;
break;
}

if (led_enable) {
switch (interface) {
case 0:
led_on(0); led_off(1);
break;
case 1:
led_on(1); led_off(0);
break;
}
if (volt < 1.5 || (volt <= 2.5 && volt < 3.5)) {
led_off(2);
}
else {
led_on(2);
}
if (freq < 1000 || (freq >= 5000 && freq < 10000)) {
led_off(3);
}
else {
led_on(3);
}
}
else {
led_off(0); led_off(1); led_off(2); led_off(3);
}
}

void key_event(uint8_t key) {
static bit press_flag = 0; // 用于判断按键是不是刚刚按下——实现一些按键的下降沿触发

if (timer_key < 50) {
return;
}
timer_key = 0;

switch (key) {
case 13: // S4
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1; // 实话说,这种写法是感觉告诉我的,,,一坨史,,,它将在default中被置为0,,,感觉是朴素的小学数学思想所致

++interface;
interface %= 2;

break;

case 9: // S5
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1;

volt_use_rb2 = !volt_use_rb2;

break;
case 5: // S6
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1;

led_enable = !led_enable;

break;

case 1: // S7
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1;

nt_enable = !nt_enable;

break;
default:
press_flag = 0;
break;
}
}

void update() {
volt = pcf_read(0x43);
if (volt_use_rb2) {
pcf_write(0x43, volt / 5.0 * 255.0);
}
else {
pcf_write(0x43, 102); // 102 -> 2.0V
}
}

//////////////////////////////////////////////////////////////////////////////

void init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);

init_ds18b20();

pcf_write(0x43, 102); // 102 -> 2.0V

led_enable = 1;
nt_enable = 1;

timer0_Init();
timer1_Init();

EA = 1;
}

void main(void) {
init();
while(1) {
draw();
update();
key_event(keys_scan());
}
}

////////////////////////////////////////////////////////////////////////////

void timer0_int(void) interrupt 1 {
freq_counter++;
}

void timer1_int(void) interrupt 3 {
nt_index %= 8;

if (nt_enable) {
if (nt_buffer[nt_index] & 0x80) { // 小数点注记方法:第八位置1
nt_showdot(nt_index, nt_buffer[nt_index] & 0x7F);
}
else {
nt_show(nt_index, nt_buffer[nt_index]);
}
}
else {
nt_show(nt_index, NT_BLANK);
}
nt_index++;

timer_key++;

freq_timer++;
if (freq_timer >= 1000) {
freq = freq_counter;
freq_counter = 0;
freq_timer = 0;
}
}

TH9

做完第九届(最多做到第八届)的就不再往前了,越往前越简单,没什么必要性了。2018年的题过于久远,不做了。

赛前复习

前面的练习让我发现,我的框架是有多么好用上考场直接先敲一遍我的框架。

TH14

赛题略。去年赛题的复杂度确实高于往期,不过还好。

我的设计思路就是在一个计时器上搞好多个计数器出来,谁到点了谁就被触发、重置。例如第一次检测到按下S9,那么我就让关于S9的计数器+1。在timer中,如果S9的计数器非0,那么在timer中每次+1;在main中,监测到S9按下之后,就判断一下S9的计数器计数,如果达到某个数值,就认定按下的时长超过了阈值,便按照题目要求清空数据记录。

这个东西的麻烦之处在于,你要搞一大堆计数器,然后分别在不同的地方放置它们。不过它考的不是算法,只是一个大模拟,可能类似于某年的猪国杀(虽然笔者没做过23333333)。

只得省三,不只是板子上的异同。关于题目,去年理解错了很多方面,甚至在提交前的10分钟内发现了自己误解了光敏触发与温湿度界面显示时机的题目要求。省三只能怪自己。

当然了,临场发挥还遇到了一点儿其他阻碍。

完整代码

23年的太麻烦了,估计要一段时间才能做完。这就……写完了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
#include <intrins.h>
#include <STC15F104E.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

//////////////////////////////////////////////////////////////////////////////

#define attach(x, y, z) P25=(x); P26=(y); P27=(z);
#define detach() P27=0; P25=0; P26=0; // 注意顺序,P27必须在前
#define write(x, y, z, dat) P0 = 0xFF; attach(x, y, z); P0 = (dat); detach();

typedef unsigned char int8_t;
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

//////////////////////////////////////////////////////////////////////////////

uint8_t timer_key;

//////////////////////////////////////////////////////////////////////////////

uint8_t led_statue = 0xFF;

#define led_on(index) led_statue &= 0xFF ^ (1 << (index) ); write(0, 0, 1, led_statue);
#define led_off(index) led_statue |= 1 << (index); write(0, 0, 1, led_statue);
#define led_invert(index) led_statue ^= 1 << (index); write(0, 0, 1, led_statue);

//////////////////////////////////////////////////////////////////////////////

#define NT_BLANK 16
#define NT_INTERVAL 17 // 间隔符
#define NT_H 18
#define NT_P 19
#define NT_U 20
#define NT_L 21
#define NT_N 22

code uint8_t nt_code[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
0xFF, 0xBF, 0x89, 0x8C, 0xC1, 0xC7, 0xC8};
uint8_t nt_buffer[8] = {NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK, NT_BLANK};
uint8_t nt_index;

#define nt_show(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)]));
#define nt_showdot(pos, dat) write(0, 1, 1, (1 << (pos))); write(1, 1, 1, (nt_code[(dat)] & 0x7F));

//////////////////////////////////////////////////////////////////////////////

uint8_t hour, minute, second;

uint8_t temper_max;
float temper_ave;
uint8_t temper;
uint8_t temper_para; // 温度参数
uint8_t temper_last; // 上一次

uint8_t humid_max;
float humid_ave;
uint8_t humid;
uint8_t humid_last; // 上一次

uint8_t trigger_counter; // 触发次数
uint8_t trigger_hour; // 触发小时
uint8_t trigger_minute; // 触发分钟
bit trigger_invalid;
bit trigger_ready; // 保证只触发一次:当且仅当在明亮环境时,trigger_ready置1;触发之后置0=>触发的条件变为:检测到电压较低&&trigger_ready==1

uint16_t timer_trigger;
uint16_t timer_s9;
uint16_t timer_freq;
uint8_t timer_l4;

uint16_t freq_counter;
uint16_t freq;

//////////////////////////////////////////////////////////////////////////////

void delay_ms(uint8_t ms) { //@12.000MHz

while (ms--) {
unsigned char data i, j;

_nop_();
_nop_();
i = 12;
j = 168;
do {
while (--j);
} while (--i);
}
}

////////////////////////////////////////////////////////////////////////////

void nt_shownum_len(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_dot(uint8_t pos, uint32_t dat, uint8_t len, uint8_t dot_pos) {
do {
if (pos == dot_pos) {
nt_buffer[pos] = (dat % 10) | 0x80;
}
else {
nt_buffer[pos] = dat % 10;
}
dat /= 10;
pos--;
} while (--len);
}

void nt_shownum_blank(uint8_t pos, uint32_t dat, uint8_t len) {
do {
nt_buffer[pos] = dat % 10;
dat /= 10;
pos--;
} while (--len && dat);
while (len--) {
nt_buffer[pos] = NT_BLANK;
pos--;
}
}

////////////////////////////////////////////////////////////////////////////

void pcf_write(uint8_t ctrl, uint8_t dat) {
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 控制字
IIC_WaitAck();
IIC_SendByte(dat); // D/A数据
IIC_WaitAck();
IIC_Stop();
}

float pcf_read(uint8_t ctrl) {
uint8_t res;
// 先写入一次,这是因为pcf8591只会在写入之后做一次A/D转换,直接读取将读到上一次转换之后的值。
IIC_Start();
IIC_SendByte(0x90); // 写地址
IIC_WaitAck();
IIC_SendByte(ctrl); // 这里需要确定一下哪个通道要被读取,AIN1是光敏电阻的传感器
IIC_WaitAck();
IIC_Stop();
// 读取数据
IIC_Start();
IIC_SendByte(0x91); // 读地址
IIC_WaitAck();
res = IIC_RecByte();// 读取1byte即可
IIC_SendAck(0);
IIC_Stop();
return res * 5.0f / 255.0f;
}

////////////////////////////////////////////////////////////////////////////

#define ds1302_r_reg_hour 0x85
#define ds1302_w_reg_hour 0x84
#define ds1302_r_reg_minute 0x83
#define ds1302_w_reg_minute 0x82
#define ds1302_r_reg_second 0x81
#define ds1302_w_reg_second 0x80

////////////////////////////////////////////////////////////////////////////

float temper_read() {
uint16_t res = 0;
uint8_t high, low;

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
Delay_OneWire(200);

init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
Delay_OneWire(200);

low = Read_DS18B20();
high = Read_DS18B20();

res = high & 0x0F;
res = (res << 8) | low;

return res * 0.0625f;
}

////////////////////////////////////////////////////////////////////////////

void e2prom_write(uint8_t addr, uint8_t* dat, uint8_t len) {
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while (len--) {
IIC_SendByte(dat[len]);
IIC_WaitAck();
}
IIC_Stop();
}

void e2prom_read(uint8_t addr, uint8_t* dat, uint8_t len) {
uint8_t i;
IIC_Start();
IIC_SendByte(0xA0); // Write Address
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();

IIC_Start(); // Restart
IIC_SendByte(0xA1); // Read Address
IIC_WaitAck();
for (i = 0; i < len; ++i) {
dat[i] = IIC_RecByte();
IIC_SendAck(0);
}
}

////////////////////////////////////////////////////////////////////////////

sbit TX = P1^0;
sbit RX = P1^1;

void sonic_send_signal() {
uint8_t i;
for (i = 0; i < 8; ++i) {
TX = 1;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); // 8个 _nop_();
TX = 0;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
}

uint8_t sonic_get_dist() {
uint16_t tim; // 计数时间

// AUXR &= 0xFB;
// TMOD &= 0xF0;
TF0 = 0;
TH0 = 0x00; // 计数器初始化
TL0 = 0x00;
sonic_send_signal();
TR0 = 1; // 开始计数
while ( (RX == 1) && (TF1 == 0) ); // 接收到信号时,RX == 1;如果计数器溢出(TF==1)则结束计时。
TR0 = 0; // 计数结束
// 当正常接收到信号时
if (TF1 == 0) {
tim = TH0;
tim = (tim << 8) | TL0;
return (uint8_t)(tim * 0.017); // distance = 0.000001s * 34000 cm/s / 2 = 0.017 cm/s
}
// 若溢出,中断标志清零,并返回0
TF0 = 0;
return 0;
}

////////////////////////////////////////////////////////////////////////////

uint8_t keys_scan() {
uint8_t temp = 0x00;
P3 = 0x30; P42 = 1; P44 = 1;
if (P3 != 0x0C || P42 != 0 || P44 != 0) {
temp = P3 | (uint8_t)P42 << 6 | (uint8_t)P44 << 7;
// delay_ms(10);
P3 = 0x0F; P42 = 0; P44 = 0;
temp |= P3;
switch (temp) {
case 0x7E: return 1;
case 0xBE: return 2;
case 0xDE: return 3;
case 0xEE: return 4;
case 0x7D: return 5;
case 0xBD: return 6;
case 0xDD: return 7;
case 0xED: return 8;
case 0x7B: return 9;
case 0xBB: return 10;
case 0xDB: return 11;
case 0xEB: return 12;
case 0x77: return 13;
case 0xB7: return 14;
case 0xD7: return 15;
case 0xE7: return 16;
default: return 0;
}
}
return 0;
}

////////////////////////////////////////////////////////////////////////////

void timer0_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x04;
TL0 = 0xFF; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
}

void timer1_Init(void) //1000微秒12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}

//////////////////////////////////////////////////////////////////////////////

uint8_t interface, interface_echo;

// 时间
void draw_time() {
nt_shownum_len(1, hour, 2);
nt_buffer[2] = NT_INTERVAL;
nt_shownum_len(4, minute, 2);
nt_buffer[5] = NT_INTERVAL;
nt_shownum_len(7, second, 2);
}

// 回显-温度
void draw_echo_temper() {
nt_buffer[0] = 0x0C;
nt_buffer[1] = NT_BLANK;
nt_shownum_len(3, temper_max, 2);
nt_buffer[4] = NT_INTERVAL;
nt_shownum_dot(7, temper_ave * 10, 3, 6);
}

// 回显-湿度
void draw_echo_humid() {
nt_buffer[0] = NT_H;
nt_buffer[1] = NT_BLANK;
nt_shownum_len(3, humid_max, 2);
nt_buffer[4] = NT_INTERVAL;
nt_shownum_dot(7, humid_ave * 10, 3, 6);
}

// 回显-时间
void draw_echo_time() {
nt_buffer[0] = 0x0F;
nt_shownum_len(2, trigger_counter, 2);
nt_shownum_len(4, trigger_hour, 2);
nt_buffer[5] = NT_INTERVAL;
nt_shownum_len(7, trigger_minute, 2);
}

// 参数
void draw_para() {
nt_buffer[0] = NT_P;
nt_shownum_blank(7, temper_para, 7); // 这里是我偷懒,不想写5个NT_BLANK赋值了,都一样,别介意
}

// 温湿度
void draw_temper_humid() {
nt_buffer[0] = 0x0E;
nt_buffer[1] = NT_BLANK;
nt_buffer[2] = NT_BLANK;
nt_shownum_len(4, temper, 2);
nt_buffer[5] = NT_INTERVAL;
if (trigger_invalid) { // AA
nt_buffer[6] = 0x0A;
nt_buffer[7] = 0x0A;
}
else {
nt_shownum_len(7, humid, 2);
}
}

void draw() {
if (timer_trigger > 0) {
draw_temper_humid();
}
else {
switch (interface) {
case 0:
draw_time();
break;
case 1:
switch (interface_echo) {
case 0:
draw_echo_temper();
break;
case 1:
draw_echo_humid();
break;
case 2:
draw_echo_time();
break;
}
break;
case 2:
draw_para();
break;
}
}
// led
if (interface == 0) {
led_on(0);
}
else {
led_off(0);
}
if (interface == 1) {
led_on(1);
}
else {
led_off(1);
}
if (timer_trigger > 0) {
led_on(2);
}
else {
led_off(2);
}

if (timer_l4 > 0) {
led_invert(3);
timer_l4 = 1;
}

if (trigger_counter >= 2) {
if (humid > humid_last && temper > temper_last) {
led_on(5);
}
else {
led_off(5);
}
}
else {
led_off(5);
}

}

void key_event(uint8_t key) {
static bit press_flag = 0; // 用于判断按键是不是刚刚按下——实现一些按键的下降沿触发

if (timer_key < 50) {
return;
}
timer_key = 0;

switch (key) {
case 13: // S4
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1; // 实话说,这种写法是感觉告诉我的,,,一坨史,,,它将在default中被置为0,,,感觉是朴素的小学数学思想所致

if (timer_trigger == 0) {
++interface;
interface %= 3;
}
if (interface == 1) {
interface_echo = 0;
}

break;

case 9: // S5
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1;

if (timer_trigger == 0 && interface == 1) {
++interface_echo;
interface_echo %= 3;
}

break;
case 14: // S8
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1;

if (timer_trigger == 0 && interface == 2) {
temper_para++;
}

break;

case 10: // S9
if (press_flag) // 只能是按下的时刻被触发,不能是
return;
press_flag = 1;

if (timer_trigger == 0 && interface == 2) {
temper_para--;
}

if (timer_trigger == 0 && interface == 1 && timer_s9 == 0) {
timer_s9++;
}

break;
default:
press_flag = 0;
timer_s9 = 0;
break;
}
}

void update() {
float volt;
if (timer_s9 >= 2000) {
timer_s9 = 0;
// 清除所有已记录的数据
temper_max = 0;
temper_ave = 0;
humid_max = 0;
humid_ave = 0;
trigger_hour = 0;
trigger_minute = 0;
trigger_counter = 0;
trigger_ready = 1;
trigger_invalid = 0;
}
volt = pcf_read(0x41);

// time
hour = Read_Ds1302_Byte(ds1302_r_reg_hour);
hour = (hour & 0x0F) + ((hour & 0x30) >> 4) * 10; // 继续偷懒
minute = Read_Ds1302_Byte(ds1302_r_reg_minute);
minute = (minute & 0x0F) + ((minute & 0x70) >> 4) * 10;
second = Read_Ds1302_Byte(ds1302_r_reg_second);
second = (second & 0x0F) + ((second & 0x70) >> 4) * 10;

if (volt <= 3 && trigger_ready) {
trigger_ready = 0;
// humid
if (freq < 200 || freq > 2000) {
trigger_invalid = 1;
}
else {
humid_last = humid;
humid = ((float)freq) * 0.0444 + 1.11;
trigger_invalid = 0;
}

// timer
if (timer_trigger == 0) {
timer_trigger++;
}

// 数据处理
if (!trigger_invalid) {
temper_last = temper;
// temper
temper = temper_read();

temper_max = temper > temper_max ? temper : temper_max;
temper_ave = (temper_ave * trigger_counter + temper) / (trigger_counter + 1);
humid_max = humid > humid_max ? humid : humid_max;
humid_ave = (humid_ave * trigger_counter + humid) / (trigger_counter + 1);

// timer
if (temper > temper_para && timer_l4 == 0) {
timer_l4++;
}
else if (temper <= temper_para) {
timer_l4 = 0;
}

trigger_hour = hour;
trigger_minute = minute;

trigger_counter++;
}
}
else if (timer_trigger >= 3000) {
timer_trigger = 0;
}
else if (volt > 4){
trigger_ready = 1;
}
}

//////////////////////////////////////////////////////////////////////////////

void init(void) {
write(0, 0, 1, 0xFF);
write(1, 0, 1, 0x00);
write(0, 1, 1, 0x00);
write(1, 1, 1, 0xFF);

init_ds18b20();

pcf_write(0x41, 0x00);

timer0_Init();
timer1_Init();

Write_Ds1302_Byte(0x8E, 0x00);
Write_Ds1302_Byte(ds1302_w_reg_second, 0x55);
Write_Ds1302_Byte(ds1302_w_reg_minute, 0x59);
Write_Ds1302_Byte(ds1302_w_reg_hour, 0x23);
Write_Ds1302_Byte(0x8E, 0x80);

temper_para = 30;

EA = 1;
}

void main(void) {
init();
while(1) {
draw();
update();
key_event(keys_scan());
}
}

////////////////////////////////////////////////////////////////////////////

void timer0_int(void) interrupt 1 {
freq_counter++;
}

void timer1_int(void) interrupt 3 {
nt_index %= 8;

if (nt_buffer[nt_index] & 0x80) { // 小数点注记方法:第八位置1
nt_showdot(nt_index, nt_buffer[nt_index] & 0x7F);
}
else {
nt_show(nt_index, nt_buffer[nt_index]);
}
nt_index++;
timer_key++;
timer_freq++;

if (timer_freq >= 1000) {
freq = freq_counter;
freq_counter = 0;
timer_freq = 0;
}

if (timer_s9 > 0) {
timer_s9++;
}
if (timer_trigger > 0) {
timer_trigger++;
}
}

计划2

从头敲一遍模板!

这里就不放代码了。

(写到这里,笔者认为自己报个stm32也无妨,但是因为去年只是抱着“顺手切个比赛”的心态报的单片机赛道)

(犯下了傲慢之罪[:doge])

计划3

看选择题真题!

作者

勇敢梧桐树

发布于

2024-03-21

更新于

2024-04-12

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×