51单片机常用子函数大全

发布时间 2023-12-10 23:16:00作者: 一郎哥哥

1 定时器0、1

模块Time01.c 代码

#include <REGX52.H>

#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1000个1ms是1s,10ms中断的话,1000改成100

void Time0_init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;		//设置定时器016位模式
	TMOD |= 0x01;		//设置定时器0模式
	TL0 = T1MS;         //initial timer0 low byte
    TH0 = T1MS >> 8;    //initial timer0 high byte
	TF0 = 0;		//清除TF0标志
	ET0=1;
	EA=1;
	PT0=0;
	TR0 = 1;		//定时器0开始计时
}

/* 主程序main.c代码
#include <REGX52.H>
#include "Time01.h"

#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1000个1ms是1s,10ms中断的话,1000改成100

void main()
{
	Time0_init();
	while(1)
	{
				
	}	
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = T1MS;                     //reload timer0 low byte
    TH0 = T1MS >> 8;  		//设置定时初始值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		P2=~P2;//执行代码
	}
}
//头文件Time01.h代码
#ifndef __TIME01_H__
#define __TIME01_H__

void Time0_Init(void);

#endif
*/

2 定时器2

模块Time2.c 代码

#include <REGX52.H>

#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1000个1ms是1s,10ms中断的话,1000改成100

void Time2_init()
{
    T2MOD = 0;		//初始化模式寄存器,16位重装
	T2CON = 0;
	RCAP2L = TL2 = T1MS;            //initial timer2 low byte
    RCAP2H = TH2 = T1MS >> 8;       //initial timer2 high byte
    ET2 = 1;                        //enable timer2 interrupt
    EA = 1;                         //open global interrupt switch
	TR2 = 1;                        //timer2 start running
}

/* 主程序main.c代码
#include <REGX52.H>
#include "Time2.h"

#define FOSC 11059200L
#define T1MS (65536-FOSC/12/1000) //1000个1ms是1s,10ms中断的话,1000改成100

void main()
{
	Time2_init();
	while(1)
	{
				
	}	
}

void tm2_isr() interrupt 5
{
    static unsigned int count;
	TF2 = 0;
    if (count-- == 0)               //1ms * 1000 -> 1s
    {
        count = 1000;               //reset counter
        P2=~P2;//执行代码       
    }
}
//头文件Time2.h代码
#ifndef __TIME2_H__
#define __TIME2_H__

void Time2_init();

#endif
*/

3 定时器1串口

Time1Uart.c

#include <REGX52.H>

void Time1Uart_Init()
{
	SCON=0x50;  //SM0 SM1为方式1,8位UART。REN为1,允许接收。TI RI为0
	PCON &= 0x80;  //SMOD保持不变为0,波特率不加倍。
	TMOD &= 0x0F;		//设置定时器1模式,不影响定时器0
	TMOD |= 0x20;		//设置定时器1,8位重装
	TL1 = 0xFA;		//设定定时初值 4800波特率,11.0592MHz
	TH1 = 0xFA;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
	EA=1;   //打开总中断
	ES=1;   //打开串口中断
}

void Uart_SendByte(unsigned char Byte) //发送一字节
{
	SBUF=Byte;
	while(TI==0);
	TI=0;
}

void Uart_SendString(char *s) //发送字符串,实参如"sun wen ping."
{
    while (*s)              //Check the end of the string
    {
        Uart_SendByte(*s++);  //Send current char and increment string ptr
    }
}

/* 
//main.c程序
#include <REGX52.H>
#include "Time1Uart.h"

void main()
{
	Time1Uart_init();
	Uart_SendString("sun wen ping!");
	Uart_SendByte(0x66);
	while(1)
	{
				
	}	
}

void UART_Routine() interrupt 4 // 函数名任意,interrupt 4 标明是串口中断处理程序
{
	if (RI)   //利用TI发送的程序,参见Time2Uart.c
    {
        RI = 0;             //Clear receive interrupt flag
        P2 = SBUF;          //P2 show UART data
    }
}
//头文件Time1Uart.h
#ifndef __TIME1UART_H__
#define __TIME1UART_H__

void Time1Uart_Init();
void Uart_SendByte(unsigned char Byte);
void Uart_SendString(char *s);

#endif
*/

4 定时器2串口

Time2Uart.c

#include <REGX52.H>
#include "intrins.h"

#define FOSC 11059200L      //时钟频率
#define BAUD 4800       //UART 波特率

#define NONE_PARITY     0   //无奇偶校验
#define ODD_PARITY      1   //奇校验
#define EVEN_PARITY     2   //偶校验
#define MARK_PARITY     3   //校验位始终为1
#define SPACE_PARITY    4   //校验位始终为0

#define PARITYBIT NONE_PARITY   //测试奇偶校验

bit busy;                   //发送忙标志

void Time2Uart_init()
{
#if (PARITYBIT == NONE_PARITY)
    SCON = 0x50;            //方式1 8位UART,波特率可变
#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
    SCON = 0xda;            //方式3,9位,波特率可变, 校验位置1,TI=1(开中断就中断一次)
#elif (PARITYBIT == SPACE_PARITY)
    SCON = 0xd2;            //方式3,9位,波特率可变, 校验位置0,TI=1
#endif

    TL2 = RCAP2L = (65536-(FOSC/32/BAUD)); //Set auto-reload vaule
    TH2 = RCAP2H = (65536-(FOSC/32/BAUD)) >> 8;
    T2CON = 0x34;           //T2作为串口发送接收波特率发生器,启动T2
    ES = 1;                 //Enable UART interrupt
    EA = 1;                 //Open master interrupt switch
}

void Uart_SendByte(unsigned char dat)
{
    while (busy);           //等待发送完
    ACC = dat;              //把字节放入寄存器ACC,其中的1的个数为奇数时 P置位 (PSW.0)
    if (P)                  //判断奇偶
    {
#if (PARITYBIT == ODD_PARITY)
        TB8 = 0;            //Set parity bit to 0
#elif (PARITYBIT == EVEN_PARITY)
        TB8 = 1;            //Set parity bit to 1
#endif
    }
    else
    {
#if (PARITYBIT == ODD_PARITY)
        TB8 = 1;            //Set parity bit to 1
#elif (PARITYBIT == EVEN_PARITY)
        TB8 = 0;            //Set parity bit to 0
#endif
    }
    busy = 1;
    SBUF = ACC;             //Send data to UART buffer
}

void Uart_SendString(char *s)
{
    while (*s)              //Check the end of the string
    {
        Uart_SendByte(*s++);     //Send current char and increment string ptr
    }
}

main.c

#include <REGX52.H>
#include "Time2Uart.h"

extern bit busy;

void main()
{
	Time2Uart_init();
	Uart_SendString("sun wen ping!");
	Uart_SendByte(0x66);
	while(1)
	{
				
	}	
}

void UART_Routine() interrupt 4 // 函数名任意,interrupt 4 标明是串口中断处理程序
{
	if (RI)
    {
        RI = 0;             //Clear receive interrupt flag
        P2 = SBUF;          //P2 show UART data
        P2_0 = RB8;         //P2.0 show parity bit
    }
    if (TI)
    {
        TI = 0;             //Clear transmit interrupt flag
        busy = 0;           //Clear transmit busy flag
    }
}

Time2Uart.h

#ifndef __T2UART_H__
#define __T2UART_H__

void Time2Uart_init();
void Uart_SendByte(unsigned char dat);
void Uart_SendString(char *s);

#endif

5 外部中断0、1(下降沿)

测试程序main.c

#include <REGX52.H>

void main()
{
	INT0 = 1;            //P3_2(int0),P3_3(int1)) 
	IT0 = 1;            //1:下降沿,0:低电平 
    EX0 = 1;                        //enable INT1 interrupt
    EA = 1;                         //open global interrupt switch
	while(1);
}

void exint1() interrupt 0           //int0:0,int1:2
{
	P2++;
}

6 T0、1下降沿计数中断

#include <REGX52.H>

void t0int() interrupt 1           //(location at 000BH)
{
    P2++;
}

void main()
{
    TMOD = 0x06;      //time0 计数模式,8位重装
    TL0 = TH0 = 0xff;   //有一个脉冲就中断
    TR0 = 1;           //timer0 start run
    ET0 = 1;            //enable T0 interrupt
    EA = 1;              //open global interrupt switch
    
    while (1);
}

7 掉电唤醒(INT0)

#include <REGX52.H>
#include "intrins.h"

void exint0() interrupt 0           //(location at 0003H)
{
}

void main()
{
	IT0 = 1;                        //set INT0 int type (1:Falling 0:Low level)
    EX0 = 1;                        //enable INT0 interrupt
    EA = 1;                         //open global interrupt switch

    while (1)
    {
        INT0 = 1;                   //ready read INT0 port
        while (!INT0);              //check INT0
        _nop_();
        _nop_();
        PCON = 0x02;                //MCU power down
        _nop_();            //INT0中断后,先进入中断程序,返回后从这条语句开始执行
        _nop_();
        P2++;
    }
}

8 独立按键用定时器1ms中断扫描

TimeKey.c

#include <REGX52.H>

unsigned char TimeKey_get_key()  //返回按键值1-4,无按键返回0
{
	unsigned char key=0;
	if(P3_1==0){key=1;} 
	if(P3_0==0){key=2;}
	if(P3_2==0){key=3;}
	if(P3_3==0){key=4;}
	return key;
}
/*
void Timer0_Routine() interrupt 1 //定时器0 1ms中断一次
{
	static unsigned int T0Count;
	static unsigned char last_key,now_key;
	
	TL0 = T1MS;                     //reload timer0 low byte
    TH0 = T1MS >> 8;  		//设置定时初始值
	T0Count++;
	if(T0Count>=20)
	{
		T0Count=0;
		last_key=now_key;
		now_key=TimeKey_get_key();
		if(last_key==0 && now_key!=0)
			P2=now_key;//执行代码,只有上次无按键,这次有按键才动作
	}
}
*/

9 矩阵按键用定时器1ms中断扫描

TimeScanKey.c

#include <REGX52.H>

#define GPIO_KEY P1 //P17-P14为行(P17在上),P13-P10为列(P13在左)

/**
  * @brief  获取矩阵按键的值
  * @param  GPIO_KEY的四位用于键盘的行,另4位用于键盘的列
  * @retval 按键的值(0-15),无按键返回255
  */
unsigned char TimeScanKey_getKey()
{
	unsigned char key=255; //无按键返回255
	GPIO_KEY = 0xf0; //四列置低
	if(GPIO_KEY!=0xf0) //有按键,高四位就不全为高电平
	{
		GPIO_KEY = 0xf0;      //先置列全为0
		switch(GPIO_KEY)
		{
			case(0x70):key=0;break; //第一行有按键
			case(0xb0):key=4;break;
			case(0xd0):key=8;break;
			case(0xe0):key=12;break;
		}
		GPIO_KEY = 0x0f;      //再置行全为0
		switch(GPIO_KEY)
		{
			case(0x07):key=key;break; //第一列有按键
			case(0x0b):key=key+1;break;
			case(0x0d):key=key+2;break;
			case(0x0e):key=key+3;break;
		}
	}
	return key;
}
/*
void Timer0_Routine() interrupt 1 //定时器0 1ms中断一次
{
	static unsigned int T0Count;
	static unsigned char last_key,now_key;
	
	TL0 = T1MS;                     //reload timer0 low byte
    TH0 = T1MS >> 8;  		//设置定时初始值
	T0Count++;
	if(T0Count>=20)
	{
		T0Count=0;
		last_key=now_key;
		now_key=TimeScanKey_getKey();
		if(last_key=255 && now_key!=255)
			P2=now_key;//执行代码,只有上次无按键,这次有按键才动作
	}
}
*/

10 数码管用定时器中断动态显示

Nixie.c

//定时器1ms中断一次,中断中调用一次,显示一位。
#include <REGX52.H>

//数码管段码表
unsigned char idata NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5e,0x79,0x71,
							0xbf,0x86,0xdB,0xcF,0xe6,0xeD,0xfD,0x87,0xfF,0xeF,0xf7,0xfc,0xb9,0xde,0xf9,0xf1,
							0x00}; //共阴数码管d0-d7对应abcdefg和dp,第一排是0-9a-f,第二排是0.-9.a.-f.,最后一排是熄灭

//数码管显示缓存区
unsigned char Nixie_Buf[8]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; //从左到右的8位led数码管的显示数据区,开始都存放熄灭段码

#define SegmentGPIO P0    //段码:P0口经过74hc245缓冲,1点亮
#define PosGPIO P2 //位选P24、P23、P22经过74HC138译码器,最左边数码管y7(对应3个全一)
void Nixie_scan(unsigned char location)  //location 显示位置(从最左到最右依次是1到8)
{
	SegmentGPIO=0x00;  //消影
	PosGPIO=(PosGPIO & 0xe3) | ((8-location)<<2); //送位选,位置1,位选应该是全一
	SegmentGPIO=NixieTable[Nixie_Buf[location-1]];
}

void Nixie_loop(void)
{
	static unsigned char i=0;
	Nixie_scan(i+1);
	i=++i & 0x07; //i值 0-7
}

/*
main程序中,extern unsigned char Nixie_Buf[];
或者在 Nixie.h unsigned char Nixie_Buf[];
*/

11 _74HC595_WriteByte

sbit RCK=P3^5;		//RCLK
sbit SCK=P3^6;		//SRCLK
sbit SER=P3^4;		//SER
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		SER=Byte&(0x80>>i);//先移位高位
		SCK=1;  //SCK上升沿移位
		SCK=0;
	}
	RCK=1;   //RCK上升沿移位寄存器输出
	RCK=0;
}
void MatrixLED_Init()
{
	SCK=0;
	RCK=0;
}

12 把字节存入内部RAM

需求:调试中,需要观察各种数据,可以用串口发出去,电脑用串口助手接收观察,但是发送需要时间,等待发送完会阻塞,影响定时中断。把数据存放到片内高128B存储区,这样速度快,不影响程序运行。

设计:片内高128B存储区开辟64字节的内存空间,程序中需要调试的地方,存入一字节,如果64字节空间用完,则不再存入新数据。定时器0设计1ms中断一次,每次中断,显示led数码管一位,20ms判断按键,16个按键,占用0,1,3三个键。0和1键:根据读取指针把RegData数组中的4字节存入显示缓存区后,0键读取指针加4,1键读取指针减4。3键,存放指针置零,这样可以从头存入。

#include <REGX52.H>
#include "Nixie.h"

unsigned char idata RegData[64]={0x00};

/**
  * @brief  根据指针RegData[64]数组4字节到显示缓存区,option参数,为1送显后指针加4,为-1送显后指针减4
  * @param  RegData[64]数组,功能参数option
  * @retval 
  */
void RegToShow(char option)
{
	static unsigned char p;
	Nixie_Buf[1]=*(RegData+p) & 0x0f;
	Nixie_Buf[0]=*(RegData+p)>>4 & 0x0f;
	Nixie_Buf[3]=*(RegData+p+1) & 0x0f;
	Nixie_Buf[2]=*(RegData+p+1)>>4 & 0x0f;
	Nixie_Buf[5]=*(RegData+p+2) & 0x0f;
	Nixie_Buf[4]=*(RegData+p+2)>>4 & 0x0f;
	Nixie_Buf[7]=*(RegData+p+3) & 0x0f;
	Nixie_Buf[6]=*(RegData+p+3)>>4 & 0x0f;
	if(option==1)
		{if(p<=59) p+=4;}
	else if(option==-1)
		{if(p>=4) p-=4;}
}

void PutU8(unsigned char u8,reset) //第二个参数为非1,存入一个字节。第二个参数为1,不理u8,p指针复位为0
{
	static unsigned char p;
	if(reset==1)
		p=0;
	else
		if(p<64)
		{
			RegData[p]=u8;
			p++;
		}
}
/* 头文件
#ifndef __REGDATA_H__
#define __REGDATA_H__

unsigned char idata RegData[];
void RegToShow(char option);
void PutU8(unsigned char u8,reset);

#endif
*/
/*main中代码
.....
PutU8(T0Count>>8,0);
PutU8(T0Count,0);
......
if(last_key==255 && now_key!=255)
		{
			if(now_key==3)  //按键值为3,复位存放指针
				PutU8(0x66,1);
			else if(now_key==0) //按键值为0,RegData区送显示后读取指针加4
				RegToShow(1);
			else if(now_key==1) //按键值为1,RegData区送显示后读取指针键减4
				RegToShow(-1);
		}
*/