蓝桥杯单片机第11届第一场省赛代码

本文详细描述了如何通过定时器驱动STM15F2K60S2单片机实现动态数码管显示,并介绍了按键控制下的模式切换、电压参数读取与存储,以及IIC通信接口的使用。文中还纠正了一个关于中断服务函数中变量声明的C语言规范问题。
摘要由CSDN通过智能技术生成
  • 在上次写完第10届的定时器实现动态数码管的相关问题后,发现没有写如何通过定时器实现动态数码管的,这次进行详细说明。同时还有以前我的代码还存在一个不影响运行的错误。

源代码

main.c

#include <STC15F2K60S2.H>
#include <iic.h>
#include <smg.h>

bit AD_flag = 1;//电压读取标准
bit x = 1;//从参数界面进入计数界面的标志
bit led_flag;//L1五秒计时标志
bit L1_flag;//L1点亮标志
bit flag;//led灯状态更改标志

unsigned char mode = 0;//模式切换标志
unsigned char AD = 0;//记录电压的数字量
unsigned char AD_temp = 0;//用于辅助计数判断
unsigned char V = 0;//电压参数,已经放大10倍
unsigned int N = 0;//储存计数值
unsigned char count = 0;//无效按键记录
//按键消抖必要延时
void Delay20ms(void)	
{
	unsigned char data i, j;

	i = 234;
	j = 115;
	do
	{
		while (--j);
	} while (--i);
}
//按键扫描函数
void KeyScan()
{
	P32 = 0;P33 = 1;
	if(P35 == 0 )                   //S13
	{
		Delay20ms();
		if(P35 ==0)
		{
			if(mode == 2)
			{
			count = 0;
			N = 0;
			}
			else
				count++;
			while(P35 ==0);
		}
	}
	if(P34 == 0)                    //S17
	{
		Delay20ms();
		if(P34 ==0)
		{
			if(mode == 1)
			{
			count = 0;
			if(V > 0)V = V - 5;
			else if(V == 0)V = 50;
			}
			else
				count++;
			while(P34 ==0);
		}
	}
	
	P32 = 1;P33 = 0;
	if(P35 == 0)                   //S12
	{
		Delay20ms();
		if(P35 ==0)
		{
			count = 0;
			if(++mode == 3)mode = 0;
			if(mode == 2)x = 1;
			while(P35 ==0);
		}
	}
	if(P34 == 0 )                  //S16
	{
		Delay20ms();
		if(P34 ==0)
		{
			if(mode == 1)
			{
			count = 0;
			if(V < 50)V = V + 5;
			else if(V == 50)V = 0;
			}
			else
				count++;
			while(P34 ==0);
		}
	}
}
//界面更改函数
void Display()
{
	switch(mode)
	{
		case 0:
			Mode0(AD);
		break;
		case 1:
			Mode1(V);
		break;
		case 2:
			Mode2(N);
		break;
	}
}
//计数处理函数
void Data_Processing()
{
	if(AD_temp * 10 / 51 >= V && V > AD * 10 / 51) 
	{
		N++;
		AD_temp = 0;
	}
}
//改变某个led灯状态而不改变其他led灯状态
void ledx(unsigned char pos,n)
{
	if(n)
		P0 = P0 | (0x01 << pos - 1);
	else
		P0 = P0 & ~(0x01 << pos - 1);
	SelectHC573(4);
}
//led灯状态处理函数
void Led()
{
	if(AD * 10 / 51 < V && led_flag == 0)
	{
		led_flag = 1;
	}
	if(L1_flag)
	{
		led_flag = 0;
		ledx(1,0);
	}
	if(AD * 10 / 51 > V)
	{
		L1_flag = 0;
		ledx(1,1);
	}
	
	if(N % 2 == 1)ledx(2,0);
	else ledx(2,1);
	
	if(count >= 3)ledx(3,0);
	else ledx(3,1);
}
//主函数
void main()
{
	Init_System();
	V = AT24C02_Read();
	while(1)
	{
		//70毫秒读取一次电压
		if(AD_flag)
		{
			AD_flag = 0;
			AD_temp  = AD;
			AD = PCF8591_Read();
			Data_Processing();
		}
		//从参数界面进入计数界面将参数存入E2PROM
		if(mode == 2 && x == 1)
		{
			x = 0;
			AT24C02_Write(V);
		}
		KeyScan();//键盘扫描
		Display();//数码管界面更改
		//led灯状态更改
		if(flag)
		{
			flag = 0;
			Led();
		}
	}
}
//定时器0中断服务函数
void Service_Timer0() interrupt 1
{
	static unsigned int i = 0,i1 = 0,i2 = 0,i3 = 0;
	TH0 = (65536 - 1000) / 256;
	TL0 = (65536 - 1000) % 256;
	
	SMG_Byte(i,Seg_Buf[i]);
	if(++i == 8)i = 0;
	
	if(++i1 == 70)
	{
		i1 = 0;
		AD_flag = 1;
	}
	
	if(led_flag)
	{
		if(++i2 == 5000)
		{
			i2 = 0;
			L1_flag = 1;
		}
	}
	
	if(++i3 == 10)
	{
		i3 = 0;
		flag = 1;
	}
}

smg.c

#include <STC15F2K60S2.H>
//不带小数点的段码表
code unsigned char Seg_Table[] = 
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e //F
};
//带小数点的段码表
code unsigned char Seg_Point[] = 
{
0x40, //0
0x79, //1
0x24, //2
0x30, //3
0x19, //4
0x12, //5
0x02, //6
0x78, //7
0x00, //8
0x10, //9
};
//数码管显示值从这里读取
unsigned char Seg_Buf[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
//锁存器选择函数
void SelectHC573(unsigned char n)
{
	switch(n)
	{
		case 4:
			P2 = (P2 & 0x1f) | 0x80;
		break;
		case 5:
			P2 = (P2 & 0x1f) | 0xa0;
		break;
		case 6:
			P2 = (P2 & 0x1f) | 0xc0;
		break;
		case 7:
			P2 = (P2 & 0x1f) | 0xe0;
		break;
	}
	P2 = (P2 & 0x1f) | 0x00;
}
//初始化无关设备和定时器0
void Init_System()
{
	P0 = 0x00;
	SelectHC573(5);
	P0 = 0xff;
	SelectHC573(4);
	
	TMOD = 0x01;
	TH0 = (65536 - 1000) / 256;
	TL0 = (65536 - 1000) % 256;
	
	EA = 1;
	ET0 = 1;
	TR0 = 1;
}
//数码管段选和位选
void SMG_Byte(unsigned char pos,value)
{
	P0 = 0xff;
	SelectHC573(7);
	P0 = 0x01 << pos;
	SelectHC573(6);
	P0 = value;
	SelectHC573(7);
	P0 = 0xff;
}
//电压界面
void Mode0(unsigned char AD)
{
	Seg_Buf[0] = 0xc1;
	Seg_Buf[1] = 0xff;
	Seg_Buf[2] = 0xff;
	Seg_Buf[3] = 0xff;
	Seg_Buf[4] = 0xff;
	
	Seg_Buf[5] = Seg_Point[AD / 51];
	Seg_Buf[6] = Seg_Table[AD * 10 / 51 % 10];
	Seg_Buf[7] = Seg_Table[AD * 100 / 51 % 10];
}
//电压参数界面,已经放大10倍
void Mode1(unsigned char V)
{
	Seg_Buf[0] = 0x8c;
	Seg_Buf[1] = 0xff;
	Seg_Buf[2] = 0xff;
	Seg_Buf[3] = 0xff;
	Seg_Buf[4] = 0xff;
	
	Seg_Buf[5] = Seg_Point[V / 10];
	Seg_Buf[6] = Seg_Table[V % 10];
	Seg_Buf[7] = Seg_Table[0];
}
//计数界面
void Mode2(unsigned int n)
{
	Seg_Buf[0] = 0xc8;
	if(n >= 1000000)Seg_Buf[1] = Seg_Table[n / 1000000];
	else Seg_Buf[1] = 0xff;
	if(n >= 100000)Seg_Buf[2] = Seg_Table[n / 100000 % 10];
	else Seg_Buf[2] = 0xff;
	if(n >= 10000)Seg_Buf[3] = Seg_Table[n / 10000 % 10];
	else Seg_Buf[3] = 0xff;
	
	if(n >= 1000)Seg_Buf[4] = Seg_Table[n / 1000 % 10];
	else Seg_Buf[4] = 0xff;
	if(n >= 100)Seg_Buf[5] = Seg_Table[n / 100 % 10];
	else Seg_Buf[5] = 0xff;
	if(n >= 10)Seg_Buf[6] = Seg_Table[n / 10 % 10];
	else Seg_Buf[6] = 0xff;
	Seg_Buf[7] = Seg_Table[n % 10];
}

smg.h

#ifndef _smg_h_
#define _smg_h_

unsigned char Seg_Buf[];
void SelectHC573(unsigned char n);
void Init_System();
void SMG_Byte(unsigned char pos,value);
void Mode0(unsigned char AD);
void Mode1(unsigned char V);
void Mode2(unsigned int n);

#endif

iic.c

#include <STC15F2K60S2.H>
#include<intrins.h>

sbit sda = P2^1;
sbit scl = P2^0;

#define DELAY_TIME	5

//
static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();		
    }
    while(n--);      	
}

//
void I2CStart(void)
{
    sda = 1;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 0;
	I2C_Delay(DELAY_TIME);
    scl = 0;    
}

//
void I2CStop(void)
{
    sda = 0;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 1;
	I2C_Delay(DELAY_TIME);
}

//
void I2CSendByte(unsigned char byt)
{
    unsigned char i;
	
    for(i=0; i<8; i++){
        scl = 0;
		I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
		I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
		I2C_Delay(DELAY_TIME);
    }
	
    scl = 0;  
}

//
unsigned char I2CReceiveByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++){   
		scl = 1;
		I2C_Delay(DELAY_TIME);
		da <<= 1;
		if(sda) 
			da |= 0x01;
		scl = 0;
		I2C_Delay(DELAY_TIME);
	}
	return da;    
}

//
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//
void I2CSendAck(unsigned char ackbit)
{
    scl = 0;
    sda = ackbit; 
	I2C_Delay(DELAY_TIME);
    scl = 1;
	I2C_Delay(DELAY_TIME);
    scl = 0; 
	sda = 1;
	I2C_Delay(DELAY_TIME);
}

void AT24C02_Write(unsigned char dat)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(0x00);
	I2CWaitAck();
	I2CSendByte(dat);
	I2CWaitAck();
	I2CStop();
}

unsigned char AT24C02_Read()
{
	unsigned char temp = 0;
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(0x00);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	temp = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
	
	return temp;
}

unsigned char PCF8591_Read()
{
	unsigned char temp = 0;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(0x03);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	temp = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
	
	return temp;
}

iic.h

#ifndef _iic_h_
#define _iic_h_

void AT24C02_Write(unsigned char dat);
unsigned char AT24C02_Read();
unsigned char PCF8591_Read();

#endif

定时器实现动态数码管

用到的所有代码

#include <STC15F2K60S2.H>
//不带小数点的段码表
code unsigned char Seg_Table[] = 
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e //F
};
//带小数点的段码表
code unsigned char Seg_Point[] = 
{
0x40, //0
0x79, //1
0x24, //2
0x30, //3
0x19, //4
0x12, //5
0x02, //6
0x78, //7
0x00, //8
0x10, //9
};
//数码管显示值从这里读取
unsigned char Seg_Buf[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
//锁存器选择函数
void SelectHC573(unsigned char n)
{
	switch(n)
	{
		case 4:
			P2 = (P2 & 0x1f) | 0x80;
		break;
		case 5:
			P2 = (P2 & 0x1f) | 0xa0;
		break;
		case 6:
			P2 = (P2 & 0x1f) | 0xc0;
		break;
		case 7:
			P2 = (P2 & 0x1f) | 0xe0;
		break;
	}
	P2 = (P2 & 0x1f) | 0x00;
}
//初始化无关设备和定时器0
void Init_System()
{
	P0 = 0x00;
	SelectHC573(5);
	P0 = 0xff;
	SelectHC573(4);
	
	TMOD = 0x01;
	TH0 = (65536 - 1000) / 256;
	TL0 = (65536 - 1000) % 256;
	
	EA = 1;
	ET0 = 1;
	TR0 = 1;
}
//数码管段选和位选
void SMG_Byte(unsigned char pos,value)
{
	P0 = 0xff;
	SelectHC573(7);
	P0 = 0x01 << pos;
	SelectHC573(6);
	P0 = value;
	SelectHC573(7);
	P0 = 0xff;
}
//电压界面
void Mode0(unsigned char AD)
{
	Seg_Buf[0] = 0xc1;
	Seg_Buf[1] = 0xff;
	Seg_Buf[2] = 0xff;
	Seg_Buf[3] = 0xff;
	Seg_Buf[4] = 0xff;
	
	Seg_Buf[5] = Seg_Point[AD / 51];
	Seg_Buf[6] = Seg_Table[AD * 10 / 51 % 10];
	Seg_Buf[7] = Seg_Table[AD * 100 / 51 % 10];
}

void Service_Timer0() interrupt 1
{
	static unsigned int i = 0;
	TH0 = (65536 - 1000) / 256;
	TL0 = (65536 - 1000) % 256;
	
	SMG_Byte(i,Seg_Buf[i]);
	if(++i == 8)i = 0;
}

  • 首先是三个数组,其中要说的是Seg_Buf数组,数码管显示的值就是从这里面进行读取。我们只要在每次切换界面时重新对Seg_Buf数组进行赋值就可以实现界面切换。
  • SelectHC573函数
    调用这个函数在每次打开对应的锁存器后就会立马关闭。因此要先更改P0端口的值在执行该函数。
  • Init_System函数没什么可讲的,提一下SMG_Byte函数中最后一行的P0 = 0xff是为解决用定时器实现动态数码管引出来的问题,同时也会引出另一问题,具体如何解决的在我的第10届代码中已经做了详细解释。
  • 中断服务函数
    先讲一下我以前的代码的不对之处,我以前都是对i进行如下定义的
unsigned int i;

运行起来并不会有任何错误,但是经过同学提醒发现这里不符合C语言的语法。因为这样定义在C语言的语法看来会导致i的初值并不确定并且每次执行完中断服务函数i值内存都会被释放,无法完成计数的功能。但是实际运行却又完成了其计数功能。 最后到处查找也没有找到原因。只能归结于编译器默认将其定义为静态变量且赋值为0。
所以其正确定义应该如下,将其定义为静态变量且赋值为0。因为静态变量的只有在程序结束才会被释放。

static unsigned int i = 0;

最后就是在中断服务函数中调用数码管的段选位选函数,每一毫秒进入一次中断服务函数,就根据i值点亮一个数码管再对i值进行0-8循环更改。

  • 因此只需要在主函数中调用不同的Mode()函数对Seg_Buf进行单独赋值就可以实现数码管的动态显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值