第十届 蓝桥杯 单片机设计与开发项目 省赛
输入:
- 频率信号输入
- 模拟电压输入
输出(包含各种显示功能):
- LED显示
- SEG显示
- DAC输出
01 数码管显示问题:数据类型
bit Seg_Disp_Mode;//0-频率显示界面 1-电压显示界面
unsigned char Freq = 6000;
/* 键盘处理函数 */
void Key_Proc()
{
if(Key_Slow_Down) return;
Key_Slow_Down = 1;//键盘减速程序
Key_Val = Key_Read();//实时读取键码值
Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿
Key_Old = Key_Val;//辅助扫描变量
}
/* 信息处理函数 */
void Seg_Proc()
{
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;//数码管减速程序
if(Seg_Disp_Mode == 0)
{
Seg_Buf[0] = 11;
Seg_Buf[2] = Freq/100000;
Seg_Buf[3] = Freq/10000%10;
Seg_Buf[4] = Freq/1000%10;
Seg_Buf[5] = Freq/100%10;
Seg_Buf[6] = Freq/10%10;
Seg_Buf[7] = Freq%10;
}
else
{
Seg_Buf[0] = 12;
}
}
这段程序里犯了一个错误:
只要是变量可能大于255的均需要用unsigned int
数据类型来声明变量,否则会出现截断现象。
因此此处的unsigned char Freq = 6000;
应该修改为unsigned int Freq = 6000;
02 Da_Write()和Ad_Read()
- Ad_Read()
/* 信息处理函数 */
void Seg_Proc()
{
unsigned char i = 2;
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;//数码管减速程序
Voltage = Ad_Read(0x43)/51.0;
if(Output_Mode == 0)
Voltage_Output = 2;
else
Voltage_Output = Voltage;
if(Seg_Mode == 0)
{
Seg_Point[5] = !Seg_Disp_Mode;
if(Seg_Disp_Mode == 1)
{
Seg_Buf[0] = 11;
Seg_Buf[2] = Freq/100000;
Seg_Buf[3] = Freq/10000%10;
Seg_Buf[4] = Freq/1000%10;
Seg_Buf[5] = Freq/100%10;
Seg_Buf[6] = Freq/10%10;
Seg_Buf[7] = Freq%10;
while(Seg_Buf[i] == 0)
{
Seg_Buf[i] = 10;
if(++i == 7) break;
}
}
else
{
Seg_Buf[0] = 12;
//熄灭上一个界面
Seg_Buf[2] = 10;
Seg_Buf[3] = 10;
Seg_Buf[4] = 10;
Seg_Buf[5] = (unsigned char)Voltage_Output;
Seg_Buf[6] = (unsigned int)(Voltage_Output*100)/10%10;//*100可能会大于255,不转换成int型可能会溢出。
Seg_Buf[7] = (unsigned int)(Voltage_Output*100)%10;
}
}
else
{
Seg_Buf[0] = 10;
Seg_Buf[1] = 10;
Seg_Buf[2] = 10;
Seg_Buf[3] = 10;
Seg_Buf[4] = 10;
Seg_Buf[5] = 10;
Seg_Buf[6] = 10;
Seg_Buf[7] = 10;
Seg_Point[5] = 0;
}
}
Voltage = Ad_Read(0x43)/51.0;
Ad_Read() 这个函数用来读取实时电压值,在信息处理函数中调用获取。
函数原型如下:
unsigned char Ad_Read(unsigned char addr)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
return temp;
}
在蓝桥杯里addr是不0x40
就是0x43
,可以试一下,判断哪个是Rb2哪个是Rb4;
这个函数的输出在0-255
区间内,要将其转换到0-5v
区间内,需要讲输出再/51
。
- Da_Write()
使用 PCF8591 测量电位器 RB2 的输出电压,并根据试题要求通过其 DAC 功 能输出该电压值。
这个可以再其他显示函数中完成输出
/* 其他显示函数 */
void Led_Proc()
{
Da_Write(Voltage_Output*51);
if(Led_Mode == 0)
{
ucLed[0] = !Seg_Disp_Mode;
ucLed[1] = Seg_Disp_Mode;
ucLed[4] = Output_Mode;
//LED3
if(Voltage<1.5||(Voltage>=2.5&&Voltage<3.5))
{ucLed[2] = 0;}
else
{ucLed[2] = 1;}
//LED4
if(Freq<1000||(Freq>=5000&&Freq<100000))
{ucLed[3] = 0;}
else
{ucLed[3] = 1;}
}
else
{ucLed[0] = ucLed[1]=ucLed[2]=ucLed[3]=ucLed[4]=ucLed[5]=ucLed[6]=ucLed[7] = 0;}
}
Da_Write(Voltage_Output*51);
需要注意的就是需要将Voltage_Output*51
将其转换到0-255
区间内。
03 频率信号的读取
在定时器1的中断服务函数中进行:
//定时器1用于中断服务,定时器0用于计数
/* 定时器0中断初始化函数 这里要改成计数的模式:中断不需要打开,不需要自动重装载*/
void Timer0Init(void) //0毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x05;//一定要记住NE555的底层在计数器初始化的时候一定要加上这句话
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
/* 定时器1中断服务函数 */
void Timer1Server() interrupt 3
{
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//键盘减速专用
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//数码管减速专用
if(++Uart_Slow_Down == 200) Uart_Slow_Down = 0;//串口减速专用
if(++Seg_Pos == 8) Seg_Pos = 0;//数码管显示专用
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
if(++Timer_1000Ms == 1000)//定时器1用来定时1s
{
Timer_1000Ms = 0;
Freq = TH0 << 8 | TL0;
TH0 = 0;
TL0 = 0;
}
}
/* 定时器1中断初始化函数 */
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
EA = 1;
}