51单片机常用子函数大全
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);
}
*/