目录

蓝桥杯单片机第十一届省赛

目录

【蓝桥杯单片机】第十一届省赛

一、真题

https://i-blog.csdnimg.cn/direct/2ba351926f4e45bc957fb77220d20f1b.png https://i-blog.csdnimg.cn/direct/b744dc38a2c34b108c117c57dba8f634.png https://i-blog.csdnimg.cn/direct/4090d7038a7a402d8ff0d5d2f8b86193.png https://i-blog.csdnimg.cn/direct/f44cc821e86149e3adab2fffa506547c.png

二、创建工程

1.在C盘以外的盘新建文件夹,并在文件夹里面创建两个文件夹Driver 和Project

2.打开keil软件,在新建工程并选择刚刚建好的project文件夹,以准考证号命名

https://i-blog.csdnimg.cn/direct/4c74380d74af47c1a4e4968560378241.png

3.选择对应的芯片型号

https://i-blog.csdnimg.cn/direct/9a368df61ae44c99b62f4a7cf53a079b.png

https://i-blog.csdnimg.cn/direct/b526eb7c88bb486c8956894eaec19817.png

4.选择否,即不创建启动文件

https://i-blog.csdnimg.cn/direct/7535f917eb944da49ff70f6807dbd1b8.png

5.勾上生成hex文件的功能

https://i-blog.csdnimg.cn/direct/83dee71c0d7244b0bab11ab091928b34.png

6.添加头文件路径

https://i-blog.csdnimg.cn/direct/bde6068ee4d24767b2bea8fba5850297.png

三、模块构建

1.编写初始化函数(init.c)

void Cls_Peripheral(void);

  1. 关闭led    led对应的锁存器由Y4C控制
  2. 关闭 和继电器 由Y5C控制

2.编写LED函数(led.c)

void Led_Disp(unsigned char ucLed);

  1. 将ucLed 取反 的值赋给P0

  2. 开启

    y4

  3. 关闭锁存器 y4

3.编写数码管函数(seg.c)

void Seg_Tran(unsigned char *pucSeg_Buf,unsigned char *pucSeg_Code);

(1)段码转换函数

  • 定义两个变量i,j
  • for循环加Switch语句进行段码转换,在资源数据包查找段码表,并根据题目要求进行段码转换
  • 注意添加空格代表都不显示
  • 注意8+4是C 不是A

void Seg_Disp(unsigned char *pucSeg_Code,unsigned char ucSeg_Pos);

(2)数码管显示函数

  • 要对数码管进行消隐 y7
  • 显示的位置 y6
  • 显示的内容 y7

4. 编写矩阵键盘代码(key.c)

unsigned char Key_Read_KBD(void);

  1. 有返回值
  2. 16个按键,要用 十六位数据类型 unsigned int
  3. 依次将每一列设置为低电平,读取P3的 低四位(&0x0f) 存储到变量Key_New里, 不 要忘记每个都左移4位,然后记得|
  4. 用Switch语句将按键按下后的值进行判断(Key_New取反 便于理解)
  5. 返回对应按键的值
  6. 不要忘记default 返回都没有按下的值 设为0

5.编写ADC代码

unsigned char PCF8591_ADC(void);

  1. 定义SCL,SDA
  2. 返回值类型
  3. 添加"intrins.h"头文件,接触nop错误
  4. 定义变量用于存储采集的电压
  5. 写入流程:开始–发送写入地址–等待应答–发送电位器地址–等待应答
  6. 读取流程:开始–发送读取地址–等待应答– 变量接收数据

    发送应答1–终止
  7. 读取地址为0x91 写入地址为0x90
  8. 电位器地址为0x43
  9. 不要忘记把 temp返回

6.编写AT24C02代码

void EEPROM_Read(unsigned char *pucBuf,unsigned char addr,unsigned char num);

  • 读取流程:开始– 发送写入地址 –等待应答– 发送写入位置

    等待应答–开始–发送读取地址–等待应答–while(num–)来逐个存储读取的数据–if判断num是否为0–

    终止

void EEPROM_Write(unsigned char *pucBuf,unsigned char addr,unsigned char num)

  • 开始–发送写入地址–等待应答–发送写入位置–等待应答– while(num–)来逐个将数据写入(加入延时保证稳定性)–

    终止

7.定时器函数编写

void Timer0Init(void);

  1. 在sti-isp软件中生成定时长度为1ms的c代码,直接复制
  2. 不要忘记打开定时器0的开关和定时器总开关

四、主函数代码

1.添加好所有头文件在主函数和工程文件夹中

2.外设初始化,定时器初始化,打开中断总开关

3.数码管函数编写

  • 定义数组和变量,数组分别为12为和8位,不加*  变量赋初值为0
  • 编写Seg_Proc();函数
  • 添加时间变量在定时器0中断进行自加
  • 200ms
  • 动态显示添加到中断里
  • 判断模式
  • 数码管转换函数不要忘

4.ADC函数编写

  • 时间200
  • 将读取的值赋值给变量,实际的值需要除以51.0是真实电压值

5.按键函数编写

  • 不用NE555时,不要短接,否则按键会失效

6.led函数编写

  • 200ms
  • 最后不要忘记调用led显示函数

五、难点解析

1.关于EEPROM 的断电保存

2.触发条件

3.指示灯 用到了ulms

4.无效按键的触发

六、主函数代码

#include <stdio.h>
#include "seg.h"
#include "led.h"
#include "init.h"
#include "key.h"
#include "iic.h"
#include "tim.h"
//Seg
unsigned char pucSeg_Buf[12],pucSeg_Code[8],ucSeg_Pos=0;
//ADC
unsigned char ucADC=0;
float ADC_Pram=0;
unsigned int uicount=0;
unsigned char ucADC_Old=0;
//led
unsigned char ucLed=0;
//key
unsigned char Key_Val=0,Key_Val_Old=0;
unsigned int uiError=0;
//EEPROM
unsigned char EEPROM_Buf[2];
//timer
unsigned long ulms=0;
unsigned long ulled=0;
unsigned int uiSeg_Dly=0;
unsigned int uiADC_Dly=0;
unsigned int uiKey_Dly=0;
unsigned int uiLed_Dly=0;

//function
void Seg_Proc(void);
void ADC_Proc(void);
void Key_Proc(void);
void Led_Proc(void);
 
//mode
unsigned char Disp_Mode=0;

void main(void)
{
	Cls_Peripheral();
	EEPROM_Read(EEPROM_Buf,0x00,1);
	ADC_Pram=EEPROM_Buf[0]/10.0;
	Timer0Init();
	EA=1;
	while(1)
	{
		Seg_Proc();
		ADC_Proc();
		Key_Proc();
		Led_Proc();
	}
}
void Led_Proc(void)
{
	if(uiLed_Dly<200)
	return;
	uiLed_Dly=0;
	
	if(((ucADC/51.0)<ADC_Pram)&&(ulms-ulled>5000))
	{
		ucLed|=0x01;
	}
	else
		
	{
	  ucLed&=~0x01;
	}
	if(uicount%2==0)
	{
		ucLed&=~0x02;
	}
	else
	{
	  ucLed|=0x02;
	}
	if(uiError>=3)
	{
		ucLed|=0x04;
	}
	else
	{
	  ucLed&=~0x04;
	}
	Led_Disp(ucLed);
}

void Seg_Proc(void)
{
	if(uiSeg_Dly<200)
	return;
	uiSeg_Dly=0;
	if(Disp_Mode==0)
	{
		sprintf(pucSeg_Buf,"U    %4.2f",ucADC/51.0);
	}
	else if(Disp_Mode==1)
	{
		sprintf(pucSeg_Buf,"P    %4.2f",ADC_Pram);
	}
	else
	{
		sprintf(pucSeg_Buf,"N%7u",uicount);
	}
	Seg_Tran(pucSeg_Buf,pucSeg_Code);
}
void ADC_Proc(void)
{
	if(uiADC_Dly<500)
	return;
	uiADC_Dly=0;
	ucADC=PCF8591_ADC();
	if(((ucADC_Old/51.0)>ADC_Pram)&&((ucADC/51.0)<=ADC_Pram))
	{
		uicount++;
		ulled=ulms;
	}
	if(((ucADC_Old/51.0)<ADC_Pram)&&((ucADC/51.0)>=ADC_Pram))
	{
		ulled=ulms;
	}
	ucADC_Old=ucADC;
}
void Key_Proc(void)
{
	if(uiKey_Dly<20)
	return;
	uiKey_Dly=0;
	
	Key_Val=Key_Read_KBD();
	if(Key_Val==Key_Val_Old)
		return;
	switch (Key_Val)
	{
		case 12:
			uiError=0;
			Disp_Mode=(Disp_Mode+1)%3;
		if(Disp_Mode==2)
		{
			EEPROM_Buf[0]=(unsigned char)(ADC_Pram*10);
			EEPROM_Write(EEPROM_Buf,0x00,1);
		}
			break;
		case 13:
			if(Disp_Mode==2)
			{
				uiError=0;
				uicount=0;
			}
			else
			{
				uiError++;
			}
		case 16:
			if(Disp_Mode==1)
			{
				uiError=0;
				if(ADC_Pram>=5)
				{
					ADC_Pram=0;
				}
				else
				{
				ADC_Pram+=0.5;
				}
			}
			else
			{
				uiError++;
			}
			break;
			case 17:
			if(Disp_Mode==1)
			{
				uiError=0;
				if(ADC_Pram<=0.0)
				{
					ADC_Pram=5.0;
				}
				else
				{
				ADC_Pram-=0.5;
				}
			}
			else
			{
				uiError++;
			}
			break;
	}
		Key_Val_Old=Key_Val;
}
void Time_0(void) interrupt 1
{
	ulms++;
	uiSeg_Dly++;
	uiADC_Dly++;
	uiKey_Dly++;
	uiLed_Dly++;
	if(ulms%2==0)
	{
		ucSeg_Pos=(ucSeg_Pos+1)%8;
		Seg_Disp(pucSeg_Code,ucSeg_Pos);
	}
}