蓝桥杯单片机各模块化代码

138译码器相关,基础模块的必要工具

//HC138端口选择
//通过前三位按位与,其他位数按位或的原理
//省去了部分HC138选端口的代码                   //最好分开写
void InitHC138(unsigned char n)
{
	switch(n)
	{
		case 4:
			P2=P2&0x1f;       
            P2=P2|0x80;                     //LED
		  break;
		case 5:                            
			P2=P2&0x1f;
            P2=P2|0xa0;                     //蜂鸣器继电器   
		  break;
		case 6:
			P2=P2&0x1f;
            P2=P2|0xc0;                     //数码管位选
		  break;
		case 7:
			P2=P2&0x1f;
            P2=P2|0xe0;                     //数码管段选
		  break;
		case 0:
			P2=P2&0x1f;
            P2=P2|0x00;                     //关闭锁存器
		  break;
	}
}


//初始化
void InitSystem()
{
	InitHC138(5);
	P0=0x00;              //初始关闭LED与蜂鸣器,防止上电出现错误
	InitHC138(4);
	P0=0xFF;
}

LED相关代码(解决了LED闪烁与数码管冲突的问题)

默认变量定义

// LED状态缓存(0:灭,1:亮)
unsigned char Led_Status = 0xFF; // 默认全灭(共阳极)

// LED模式定义(0:常亮/常灭,1:闪烁)
unsigned char Led_mode[8] = {0}; // 8个LED的模式

// 闪烁计数器(控制闪烁频率)
unsigned int Blink_counter = 0;
unsigned char Blink_Time=100 // 闪烁间隔(单位:ms)

功能代码

将上段代码放入定时器定期扫描,下段代码则是真正起到设置作用的代码

void UpdateLEDs()
	{
    // 仅在需要时操作硬件(避免频繁调用138)
    static unsigned char Last_Led_Status = 0xFF;
    if (Led_Status != Last_Led_Status) 
	{ 
        InitHC138(4); // 选择Y4(LED锁存器)
        P0 = Led_Status;        // 写入LED状态
		InitHC138(0);              // 关闭锁存器(Y0)      //不关闭锁存器就会与数码管冲突
        Last_Led_Status = Led_Status;
    }
}

// 设置LED模式(led_num: 0~7,mode: 0=常亮/常灭,1=闪烁)
void SetLedMode(unsigned char Led_Num, unsigned char mode, unsigned char state) {
    if (Led_Num > 7) return;
    Led_mode[Led_Num] = mode;
    
    // 初始化状态(若为常亮/常灭)
    if (mode == 0) 
		{
        if (state) 
		  {
            Led_Status |= (1 << Led_Num);   // 灭
          } 
        else 
          {
            Led_Status &= ~(1 << Led_Num);  // 亮
          }
    }
    UpdateLEDs(); // 立即更新硬件
}
//闪烁功能由此实现,定期翻转亮灭状态,控制对应LED位置亮灭
if (++ms_counter >= Blink_Time) 
	{
     ms_counter = 0;
     Blink_counter ^= 1;                            // 翻转闪烁状态(0或1)

     for (i = 0; i < 8; i++) 
			{
     if (Led_mode[i] == 1)                      // 遍历所有LED,更新闪烁模式的状态
			{                                  // 闪烁模式
            if (Blink_counter) 
			   {
                 Led_Status |= (1 << i);  // 灭
               } 
				 else 
			   {
                    Led_Status &= ~(1 << i); // 亮
               }
            }
        }
        UpdateLEDs(); // 统一更新硬件
    }

实例演示

 SetLedMode(0,0,0);
 SetLedMode(7,1,0);	
//亮灭需自主控制,不会自动关闭

独立按键扫描

unsigned char Key()
{
	unsigned char temp=0;
	if(P33==0)
	{
		  temp=4;
	}
	if(P32==0)
	{
		  temp=5;
	}
	if(P31==0)
	{

		  temp=6;
	}
	if(P30==0)
	{	
		  temp=7;
	}
	return temp;
}


矩阵按键扫描

unsigned char MartixScan()
{
	unsigned char temp=0;
	P44=0;P42=1;P35=1;P34=1;
	if(P33==0) temp=4;
	if(P32==0) temp=5;
	if(P31==0) temp=6;
	if(P30==0) temp=7;
	
	P44=1;P42=0;P35=1;P34=1;
	if(P33==0) temp=8;
	if(P32==0) temp=9;
	if(P31==0) temp=10;
	if(P30==0) temp=11;
	
	P44=1;P42=1;P35=0;P34=1;
	if(P33==0) temp=12;
	if(P32==0) temp=13;
	if(P31==0) temp=14;
	if(P30==0) temp=15;
	
	P44=1;P42=1;P35=1;P34=0;
	if(P33==0) temp=16;
	if(P32==0) temp=17;
	if(P31==0) temp=18;
	if(P30==0) temp=19;
	
	return temp;
}

按键下降沿与上升沿判断

void Key_Proc()
{
   KeyN=MartixScan();                  //按键下降沿与上升沿的判断
   Key_Down=KeyN&(Key_Old^KeyN);
   Key_Up=~KeyN&(Key_Old^KeyN);
   Key_Old=KeyN;
 switch(Key_Down)
   {
     case 4://此处填写作用
     break;
     case 5:...
   }
 switch(Key_Up)
   {
     同上
   }

切记不能忘记break

数码管

void SMG(unsigned char num,plc)
{
	SMGBuff[plc]=num;                    //对数码管缓存数组进行改变
		
}

void SMG_Set(unsigned char ad1,ad2,ad3,ad4,ad5,ad6,ad7,ad8)
{
	if(SMG_Flag)                         //此为减速标志位
	{
	SMG(ad1,1);
	SMG(ad2,2);                          //一次性对所有数码管缓存数组进行改变
	SMG(ad3,3);
	SMG(ad4,4);
	SMG(ad5,5);
	SMG(ad6,6);
	SMG(ad7,7);
	SMG(ad8,8);
	SMG_Flag=0;
	}
}

void SMG_Show()                  //放在定时器里扫描,显示的是八个数码管缓存数组
{
	InitHC138(7);                //消隐
	P0 = 0xff;                     
	InitHC138(6);
	P0=0x01<<(sign-1);
    InitHC138(0);                  //消隐
	P0 = 0xff;	                   
	InitHC138(7);
	P0=SMGNB[SMGBuff[sign]];       
	sign++;
	if(sign>8)                     
	{                            
		sign=0;	                    
	}
}

蜂鸣器继电器

void Buzzer_Proc()
{
	InitHC138(5);
	P0=0x50;
	InitHC138(0);
}

//比较简单,0x50是蜂鸣器与继电器,0x10是继电器,0x40是蜂鸣器
//0x00是全关

定时器(可直接在STC-ISP中生成)

这是定时器0(处理一般功能)

void Timer0_Working()  interrupt 1        //定时器0的端口
{
	static unsigned int SMG_Count=0;      //static代表局部变量
    static unsigned int Key_Pos=0;        //注意int与char的限制大小
	static unsigned char SMG_Delay=0;
    TL0=0x18;                             //非自动重载,需要进入中断时重设定时器初值
    TH0=0xFC;
    if(++Key_Pos == 10)
	{
	Key_Pos=0;                            //按键扫描
	Key_Flag=1;                           //当Key_Flag为1时执行按键相关
	}
	if(++SMG_Count==2)
    { 
	SMG_Show();                           //数码管扫描
	SMG_Count=0;
    }
    if(++SMG_Delay==80)                   //数码管减速,防止刷新过快
    {
	SMG_Flag=1;
	SMG_Delay=0;
    } 

如上图演示为部分定时器功能(不要忘记打开总中断与定时器中断)

DS1302(时钟模块)

赛点资源包中缺少的部分定义

sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST=P1^3;

实际使用

xdata unsigned char Write_Ds1302_ADD[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; // DS1302写地址
xdata unsigned char Read_Ds1302_ADD[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; // DS1302读地址
xdata unsigned char Time[7]={0x00,0x00,0x00,0x04,0x03,0x06,0x25}; // 时间数组,格式为秒、分、时、日、月、星期、年


// DS1302初始化函数
void Init_Ds1302()     //需要放在主函数的while循环前,进行初始化
{
    char i;
    Write_Ds1302_Byte(0x8e,0x00); // 关闭写保护
    for(i=0;i<7;i++)
    {
        Write_Ds1302_Byte(Write_Ds1302_ADD[i],Time[i]); // 写入初始时间
    }        
    Write_Ds1302_Byte(0x8e,0x80); // 打开写保护
}


//DS1302读取时间,存入Time数组
void Read_Ds1302_Time()
{
    char i;
    for(i=0;i<7;i++)
    {
        Time[i]=Read_Ds1302_Byte(Read_Ds1302_ADD[i]); // 读取时间
    }
}

DS18B20(温度读取模块)

赛点资源包中缺少的端口定义

sbit DQ=P1^4;

功能代码

/* DS18B20温度读取函数 */
int Read_DS18B20_temp(unsigned int Temp)       //int型数据,不是char型数据!!!
{
    unsigned char LSB, MSB;
    init_ds18b20();             // 初始化DS18B20
    Write_DS18B20(0xCC);        // 跳过ROM指令
    Write_DS18B20(0x44);        // 启动温度转换
    
    init_ds18b20();             // 重新初始化
    Write_DS18B20(0xCC);
    Write_DS18B20(0xBE);        // 读取暂存器
    LSB = Read_DS18B20();       // 读取温度低字节
    MSB = Read_DS18B20();       // 读取温度高字节
    init_ds18b20();             // 释放总线

    Temp = (MSB << 8) | LSB;    // 合成16位温度数据
    return (Temp/16*10);
}



//需注意是int型数据,不要搞错了

主函数中如此写

Temp=Read_DS18B20_temp(Temp);

PCF8591(光敏电阻读取,与电压输入输出)

赛点资源包缺少的端口定义

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

功能代码

void PCF8591_DAout(unsigned char Data)
{
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();                             //电压输出
	I2CSendByte(0x40);
	I2CWaitAck();
	I2CSendByte(Data);
	I2CWaitAck();
	I2CStop();  
	
}

unsigned char PCF8591_ADin(unsigned char addr)
{
	unsigned char ad;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	                                            //电压或光敏电阻数据采集
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	ad=I2CReceiveByte();
	I2CSendAck(1);  
    I2CStop();  
	return ad;
}

实际使用

Light=PCF8591_ADin(0x01);  //光敏电阻电压采集

ad=PCF8591_ADin(0x03);     //可控电阻电压采集 
0-255对应0-5V

PCF8591_DAout(0);
PCF8591_DAout(51); 
PCF8591_DAout(102);      //输出电压值

//近似255/5的每单位电压输出值



//0x41通道可能是混合采集
//只AD时,可只用0x01,0x03。

//只DA时,可只用0x40。

//既要AD又要DA时,需用0X41,0X43。

AT24C02(存储模块)

赛点资源包中少的端口定义

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

功能代码(做到断电存储)

void AT24C02_Write(unsigned char addr,unsigned char Data)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();                            //AT24C02写地址代码
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(Data);
	I2CWaitAck();
	I2CStop();
	Delay(5);
}

unsigned char AT24C02_Read(unsigned char addr)
{
	unsigned char Data;
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();                           //AT24C02读地址代码
	
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	
	Data=I2CReceiveByte();
	I2CSendAck(1);//不应答
	I2CStop();
	Delay(5);
	return Data;
}
	

超声波模块

超声波发送数据与接收数据定义

sbit Trig=P1^0;
sbit Echo=P1^1;

超声波专用定时器初始化

void Wave_timer_init(void)		//12.000MHz
{
	CMOD=0x00;
	CL=0;
	CH=0;
	CF=0;
	CR=0;
}

超声波初始化

void Wave_Init()
{
unsigned char i;
	for(i=0;i<8;i++)
	{
		Trig=1;                  //超声波初始化  产生八个方波信号
		Delay12us();
		Trig=0;
		Delay12us();             //延时一定时间,方波频率计算得到
	}
}

超声波读取数据

unsigned char Wave_Read()              //超声波读取数据
{
	unsigned int Time;
	Wave_timer_init();                  //超声波所使用的定时器的重启
	Wave_Init();                        //发送信号
	CR=1;                              //开始计时
	while((Echo==1)&&(CF==0));          //接收到数据之后停止计时(且数据未溢出)
	CR=0;	
	if(CF==0)                          //如果计时停止
	{
		Time=CH<<8|CL;                  //得到距离数据
    return(Time*0.017);		            //音速与频率的计算得到距离
	}
	else
	{
		CF=0;                            //错误数据(定时器溢出)
		return 0;
	}
}

使用

Wave_Data=Wave_Read();

NE555(频率模块)

初始值

int Count_f,Dat_f;

数据较大,别忘了是int

频率定时器初始化(八位自动重载)

void Timer_Init(void)	
{
TMOD=0x16;
ET0=1;        //打开定时器0开关
ET1=1;				//打开定时器1开关
EA=1;					//打开EA总开关
TH0=0XFF;			//设置初值,使P3^4每产生一个脉冲,触发一次定时器0中断
TL0=0XFF;
TH1=0XFC;			//设置初值,定时器1每1ms产生一次中断
TL1=0X18;
TR0=1;				//定时器0开始计时
TR1=1;				//定时器1开始计时
}


void Timer0_Working()  interrupt 1
{
	Count_f++;
}
void Timer1_Working()  interrupt 3
{
    TL1=0x18;
	TH1=0xFC;
	if(++Count_t==1000) 
	{                             //此为频率值的产生
		Dat_f=Count_f;            //时间不同,频率的大小也不同
		Count_f=0;
		Count_t=0;
	}
//其他无关功能此处省略
}

此处使用定时器0产生频率,定时器1处理功能,相反应该不行

另一个定时器仍为非自动重载,所以别忘记设置初值

串口通讯模块(难点)

串口使用的数组与索引

unsigned char Uart_Data[10]={0};//串口接收数组
unsigned char Uart_Index;//串口接收索引

定时器2初始化

void UartInit(void)		//9600bps@12.000MHz
{
	SCON = 0x50;		//8位数据,可变波特率
	AUXR |= 0x01;		//串口1选择定时器2为波特率发生器
	AUXR &= 0xFB;		//定时器2时钟为Fosc/12,即12T
	T2L = 0xE6;		//设定定时初值
	T2H = 0xFF;		//设定定时初值
	AUXR |= 0x10;		//启动定时器2
	EA=1;              //总中断
    ES=1;             //串口中断
}  

中断存入数组

void Uart_Server() interrupt 4
{                            //没有中断则无法起到串口作业
	if(RI==1)                  //因为进入中断会卡死
	{
	 Uart_Data[Uart_Index]=SBUF;
     Uart_Index++;
     RI=0; 
	}
}

 串口发送字符与字符串

void SendByte(unsigned char Dat)
{
	SBUF=Dat;
	while(TI==0);  //发送完会变成1
	TI=0;	       //手动清零           
}

void SendString(unsigned char *str)
{
       while(*str !='\0')
     {                                   //发送字符串
        SendByte(*str++);
     }
}

 使用例子

void Uart_Proc()
{
	if(Uart_Flag==1)              //这是逻辑判断,根据情况设置使用
	{
	  SendString("Data:");         //此处为例子,发送字符串与发送数字数据的方法
	  SendByte(0x30+Light%10);
	  SendString("\r\n");         //换行符
	  Uart_Flag=0;
	}
	else if(Uart_Flag2==1)       //另一个逻辑
	{
	  SendString("Warn\r\n"); 
      Uart_Flag2=0;		
	}
	if(Uart_Index!=0)            //如果索引不为零(开始接收字符)
    {
	if(Uart_Index==5)
	{
		if(Uart_Data[0]=='C' && Uart_Data[1]=='l' && Uart_Data[2]=='o' && Uart_Data[3]=='s' && Uart_Data[4]=='e')               //判断存入缓存数组的字符
		{	
			Uart_Time=0;
			Warn_Flag=0;            //起到作用
	 	}
	}
	Uart_Index=0;                //索引归零
  }   
}      

此为蓝桥杯单片机所有考察模块,记录以便日后查看

### 蓝桥杯单片机竞赛中的模块化编程示例 在蓝桥杯单片机竞赛中,采用模块化编程可以提高代码的可读性和维护性。以下是几个常见的模块及其对应的代码片段。 #### 1. 初始化模块 初始化模块负责设置硬件参数以及配置外设接口。这通常包括时钟配置、GPIO初始化等内容。 ```c void System_Init(void){ // 设置系统时钟频率为72MHz RCC_Configuration(); // 配置LED连接的IO口模式 GPIO_Configuration(); } ``` 此部分确保了系统的稳定运行环境[^1]。 #### 2. 中断服务程序 (ISR) 中断处理函数用于响应外部事件或者定时器触发的动作,在实时控制系统中非常重要。 ```c void EXTI0_IRQHandler(void){ if(EXTI_GetITStatus(EXTI_Line0)!= RESET){ // 清除标志位 EXTI_ClearITPendingBit(EXTI_Line0); // 执行按键按下后的操作逻辑 Key_Pressed_Action(); } } ``` 这段代码展示了如何编写一个简单的外部中断服务程序来检测按钮按下的情况[^2]。 #### 3. 功能实现模块 功能实现模块专注于具体的应用场景需求,比如控制电机转动方向、显示温度数值等实际任务。 ```c void Motor_Control(int direction){ switch(direction){ case FORWARD: // 向前运动的相关指令集 break; case BACKWARD: // 向后退的相关指令集 break; default : // 默认停止状态 STOP_MOTOR; } } ``` 上述例子说明了一个基本的方向控制算法,适用于小型机器人或其他自动化设备上的应用开发。 通过以上这些不同类型的模块组合在一起就可以构建完整的嵌入式应用程序框架,从而更好地应对像蓝桥杯这样的比赛挑战。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值