main.c

/*
*******************************************************************************
*                     《手把手教你学51单片机(C语言版)》
*                    配套 KST-51 单片机开发板 示例源代码
*
*         (c) 版权所有 2014 金沙滩工作室/清华大学出版社 保留所有权利
*                 获取更多资料请访问:http://www.kingst.org
*
* 文件名:main.c
* 描  述:第18章 作业题3 Modbus协议实现校时
* 版本号:v1.0.0
* 备  注:
* 1、在lesson15_3的基础上去掉按键校时,添加lesson18_2中的Modbus协议支持
* 2、利用Modbus调试精灵的写寄存器功能,可修改日期时间的每一个字节
* 3、寄存器地址0x0000~0x0006分别对应“年/月/日/时/分/秒/星期”
* 4、RS485方向控制信号由原来的P1.7改为P2.0,因本例使用了DS1302而未使用按键
*******************************************************************************
*/#include <reg51.h>
#include <oled.h>sbit LED=P2^6;sbit LED0=P0^0;
sbit LED1=P0^1;
sbit LED2=P0^2;
sbit LED3=P0^3;
sbit LED4=P0^4;
sbit LED5=P0^5;
sbit LED6=P0^6;
sbit LED7=P0^7;
sbit BUZZ = P1^6;
bit flagBuzzOn = 0;   //蜂鸣器启动标志
bit flagLED0On=0;   //LED00标志
bit flagLED1On=0;   //LED01标志
bit flagLED2On=0;   //LED02标志
bit flagLED3On=0;   //LED03标志
bit flagLED4On=0;   //LED04标志
bit flagLED5On=0;   //LED05标志unsigned char regGroup[5];  //Modbus寄存器组,地址为0x00~0x04
bit flag200ms = 1;  //200ms定时标志
bit reqRefresh = 0;  //时间刷新请求
unsigned char T0RH = 0;  //T0重载值的高字节
unsigned char T0RL = 0;  //T0重载值的低字节void ConfigTimer0(unsigned int ms);
void RefreshTimeShow();
extern void UartDriver();
extern void ConfigUART(unsigned int baud);
extern void UartRxMonitor(unsigned char ms);
extern void UartWrite(unsigned char *buf, unsigned char len);
extern unsigned int GetCRC16(unsigned char *ptr,  unsigned char len);
void Delay500ms()       //@11.0592MHz
{unsigned char i, j, k;i = 4;j = 129;k = 119;do{do{while (--k);} while (--j);} while (--i);
}void Delay_tms(int ms)
{                         int i;int mmm;for(i=0;i<100;i++)for(mmm=ms;mmm>0;mmm--);
}void main()
{int i=0;for(i=0;i<3;i++){LED=1;LED0=1;LED1=1;LED2=1;LED3=1;LED4=1;LED5=1;LED6=1;LED7=1;Delay_tms(200);LED=0;LED0=0;LED1=0;LED2=0;LED3=0;LED4=0;LED5=0;LED6=0;LED7=0;Delay_tms(200);}EA = 1;            //开总中断ConfigTimer0(1);   //配置T0定时1msConfigUART(9600);  //配置波特率为9600OLED_Init();Delay_tms(10);OLED_ShowNum(0,0,125,3,16);//显示整型Delay_tms(1000);OLED_Clear();Delay_tms(20);// OLED_ShowChar(40,0,'A' ,16);//显示字符4*8,0,列,行---666 AOLED_ShowChar(0,0,'X' ,16);//显示字符OLED_ShowChar(8,0,'0' ,16);//显示字符OLED_ShowChar(16,0,'6' ,16);//显示字符OLED_ShowChar(24,0,'=' ,16);//显示字符OLED_ShowChar(32,0,'X' ,16);//显示字符OLED_ShowChar(40,0,'X' ,16);//显示字符OLED_ShowChar(56,0,'X' ,16);//显示字符OLED_ShowChar(64,0,'0' ,16);//显示字符OLED_ShowChar(72,0,'5' ,16);//显示字符OLED_ShowChar(80,0,'=' ,16);//显示字符OLED_ShowChar(88,0,'X' ,16);//显示字符Delay_tms(10);while (1){UartDriver();  //调用串口驱动}
}/* 串口动作函数,根据接收到的命令帧执行响应的动作buf-接收到的命令帧指针,len-命令帧长度 */
void UartAction(unsigned char *buf, unsigned char len)
{unsigned int  crc;unsigned char cnt;unsigned char str[4];unsigned char crch, crcl;unsigned char i;if (buf[0] != 0x01) //本例中的本机地址设定为0x01,{                   //如数据帧中的地址字节与本机地址不符,return;         //则直接退出,即丢弃本帧数据不做任何处理}//地址相符时,再对本帧数据进行校验crc = GetCRC16(buf, len-2);  //计算CRC校验值crch = crc >> 8;crcl = crc & 0xFF;if ((buf[len-2]!=crch) || (buf[len-1]!=crcl)){return;   //如CRC校验不符时直接退出}//地址和校验字均相符后,解析功能码,执行相关操作switch (buf[1]){case 0x03:  //读取一个或连续的寄存器//01+03+00+00if ((buf[2]==0x00) && (buf[3]<=0x05)) //只支持0x0000~0x0005{if (buf[3] <= 0x04){i = buf[3];      //提取寄存器地址cnt = buf[5];    //提取待读取的寄存器数量buf[2] = cnt*2;  //读取数据的字节数,为寄存器数*2len = 3;         //帧前部已有地址、功能码、字节数共3个字节while (cnt--){buf[len++] = 0x00;          //寄存器高字节补0buf[len++] = regGroup[i++]; //寄存器低字节}}else  //地址0x05为蜂鸣器状态{buf[2] = 2;  //读取数据的字节数buf[3] = 0x00;buf[4] = flagBuzzOn;len = 5;}break;}else  //寄存器地址不被支持时,返回错误码{buf[1] = 0x83;  //功能码最高位置1buf[2] = 0x02;  //设置异常码为02-无效地址len = 3;break;}//-----------------------------------------------------------------------------         case 0x06:  //写入单个寄存器if ((buf[2]==0x00) && (buf[3]<=0x05)) //只支持0x0000~0x0005{if (buf[3] <= 0x04){i = buf[3];             //提取寄存器地址regGroup[i] = buf[5];   //保存寄存器数据//    OLED_ShowChar(40,0,regGroup[i] ,16);//显示字符cnt = regGroup[i] >> 4; //显示到液晶上if (cnt >= 0xA)str[0] = cnt - 0xA + 'A';elsestr[0] = cnt + '0';cnt = regGroup[i] & 0x0F;if (cnt >= 0xA)str[1] = cnt - 0xA + 'A';elsestr[1] = cnt + '0';str[2] = '\0';//显示寄存器的数据OLED_ShowChar(32,0, str[0] ,16);//显示字符OLED_ShowChar(40,0, str[1] ,16);//显示字符}else  //地址0x05为蜂鸣器状态{flagBuzzOn = (bit)buf[5]; //寄存器值转为蜂鸣器的开关}len -= 2; //长度-2以重新计算CRC并返回原帧break;}else  //寄存器地址不被支持时,返回错误码{buf[1] = 0x86;  //功能码最高位置1buf[2] = 0x02;  //设置异常码为02-无效地址len = 3;break;}//---------------------------------------------------------------------------------线圈设置//-----------------------------------------------------------------------------         case 0x05:  //写入单个线圈//0x01+0x05+线圈高地址+线圈低地址+线圈状态高数据+线圈状态低数据+CRC低+CRC高//01+05+00+00/01/02/03/04/05+//只支持0x0000~0x0005共6个线圈if ((buf[2]==0x00) && (buf[3]<=0x05)){i = buf[3];      //提取寄存器地址if(buf[3]==0x00){flagLED0On = (bit)buf[4]; //寄存器值转为LED0的开关 OLED_ShowChar(88,0,'0' ,16);//显示字符// OLED_ShowChar(96,0,buf[5]+0x30 ,16);//显示字符OLED_ShowNum(96,0,buf[4],1,16);//显示整型5OLED_ShowNum(104,0,buf[5],1,16);//显示整型5//buf4=0x05,buf5=0x00
//buf4=0x00,buf5=0x00                                 }else if(buf[3]==0x01){flagLED1On = (bit)buf[4]; //寄存器值转为LED0的开关                                 }else if(buf[3]==0x02){flagLED2On = (bit)buf[4]; //寄存器值转为LED0的开关                                 }else if(buf[3]==0x03){flagLED3On = (bit)buf[4]; //寄存器值转为LED0的开关                                 }else if(buf[3]==0x04){flagLED4On = (bit)buf[4]; //寄存器值转为LED0的开关                                 }else if(buf[3]==0x05){flagLED5On = (bit)buf[4]; //寄存器值转为LED0的开关         }len -= 2; //长度-2以重新计算CRC并返回原帧break;}else  //寄存器地址不被支持时,返回错误码{buf[1] = 0x86;  //功能码最高位置1buf[2] = 0x02;  //设置异常码为02-无效地址len = 3;break;}//***********************88888888888888default:  //其它不支持的功能码buf[1] |= 0x80;  //功能码最高位置1buf[2] = 0x01;   //设置异常码为01-无效功能len = 3;break;}crc = GetCRC16(buf, len); //计算返回帧的CRC校验值buf[len++] = crc >> 8;    //CRC高字节buf[len++] = crc & 0xFF;  //CRC低字节UartWrite(buf, len);      //发送返回帧
}
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)
{unsigned long tmp;  //临时变量tmp = 11059200 / 12;      //定时器计数频率tmp = (tmp * ms) / 1000;  //计算所需的计数值tmp = 65536 - tmp;        //计算定时器重载值tmp = tmp + 33;           //补偿中断响应延时造成的误差T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节T0RL = (unsigned char)tmp;TMOD &= 0xF0;   //清零T0的控制位TMOD |= 0x01;   //配置T0为模式1TH0 = T0RH;     //加载T0重载值TL0 = T0RL;ET0 = 1;        //使能T0中断TR0 = 1;        //启动T0
}
/* T0中断服务函数,执行按键扫描和200ms定时 */
void InterruptTimer0() interrupt 1
{static unsigned char tmr200ms = 0;TH0 = T0RH;  //重新加载重载值TL0 = T0RL;UartRxMonitor(1);  //串口接收监控if (flagBuzzOn)  //执行蜂鸣器鸣叫或关闭LED = ~LED;elseLED = 1;if (flagLED0On)  //执行蜂鸣器鸣叫或关闭LED0 = ~LED0;elseLED0 = 1;if (flagLED1On)  //执行蜂鸣器鸣叫或关闭LED1= ~LED1;elseLED1 = 1;if (flagLED2On)  //执行蜂鸣器鸣叫或关闭LED2= ~LED2;elseLED2 = 1;if (flagLED3On)  //执行蜂鸣器鸣叫或关闭LED3= ~LED3;elseLED3 = 1;if (flagLED4On)  //执行蜂鸣器鸣叫或关闭LED4 = ~LED4;elseLED4 = 1;if (flagLED5On)  //执行蜂鸣器鸣叫或关闭LED5 = ~LED5;elseLED5 = 1;}

CRC16.c

/*
*******************************************************************************
*                     《手把手教你学51单片机(C语言版)》
*                    配套 KST-51 单片机开发板 示例源代码
*
*         (c) 版权所有 2014 金沙滩工作室/清华大学出版社 保留所有权利
*                 获取更多资料请访问:http://www.kingst.org
*
* 文件名:CRC16.c
* 描  述:通用的CRC16校验算法文件
* 版本号:v1.0.0
* 备  注:
*******************************************************************************
*//* CRC16计算函数,ptr-数据指针,len-数据长度,返回值-计算出的CRC16数值 */
unsigned int GetCRC16(unsigned char *ptr,  unsigned char len)
{ unsigned int index;unsigned char crch = 0xFF;  //高CRC字节unsigned char crcl = 0xFF;  //低CRC字节unsigned char code TabH[] = {  //CRC高位字节值表0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40  } ;  unsigned char code TabL[] = {  //CRC低位字节值表0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,  0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,  0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,  0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,  0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,  0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,  0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,  0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,  0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,  0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,  0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,  0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,  0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,  0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,  0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,  0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,  0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,  0x43, 0x83, 0x41, 0x81, 0x80, 0x40  } ;while (len--)  //计算指定长度的CRC{index = crch ^ *ptr++;crch = crcl ^ TabH[index];crcl = TabL[index];}return ((crch<<8) | crcl);
}

RS485.c

/*
*******************************************************************************
*                     《手把手教你学51单片机(C语言版)》
*                    配套 KST-51 单片机开发板 示例源代码
*
*         (c) 版权所有 2014 金沙滩工作室/清华大学出版社 保留所有权利
*                 获取更多资料请访问:http://www.kingst.org
*
* 文件名:RS485.c
* 描  述:RS485通信驱动模块
* 版本号:v1.0.0
* 备  注:在标准UART驱动的基础上添加RS485的方向控制功能
*         可视具体设计需求灵活改变RS485_DIR所指向的IO口
*******************************************************************************
*/#include <reg52.h>
#include <intrins.h>sbit RS485_DIR = P3^7;  //RS485方向选择引脚bit flagFrame = 0;  //帧接收完成标志,即接收到一帧新数据
bit flagTxd = 0;    //单字节发送完成标志,用来替代TXD中断标志位
unsigned char cntRxd = 0;   //接收字节计数器
unsigned char pdata bufRxd[64];  //接收字节缓冲区extern void UartAction(unsigned char *buf, unsigned char len);/* 串口配置函数,baud-通信波特率 */
void ConfigUART(unsigned int baud)
{RS485_DIR = 0; //RS485设置为接收方向SCON  = 0x50;  //配置串口为模式1TMOD &= 0x0F;  //清零T1的控制位TMOD |= 0x20;  //配置T1为模式2TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
//     TL1 = TH1;     //初值等于重载值
//  TL1 = 0xE8;        //设定定时初值
//  TH1 = 0xFF;        //设定定时初值ET1 = 0;       //禁止T1中断ES  = 1;       //使能串口中断TR1 = 1;       //启动T1
}
/* 软件延时函数,延时时间(t*10)us */
void DelayX10us(unsigned char t)
{do {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();} while (--t);
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void UartWrite(unsigned char *buf, unsigned char len)
{RS485_DIR = 1;  //RS485设置为发送while (len--)   //循环发送所有字节{flagTxd = 0;      //清零发送标志SBUF = *buf++;    //发送一个字节数据while (!flagTxd); //等待该字节发送完成}DelayX10us(5);  //等待最后的停止位完成,延时时间由波特率决定RS485_DIR = 0;  //RS485设置为接收
}
/* 串口数据读取函数,buf-接收指针,len-指定的读取长度,返回值-实际读到的长度 */
unsigned char UartRead(unsigned char *buf, unsigned char len)
{unsigned char i;if (len > cntRxd)  //指定读取长度大于实际接收到的数据长度时,{                  //读取长度设置为实际接收到的数据长度len = cntRxd;}for (i=0; i<len; i++)  //拷贝接收到的数据到接收指针上{*buf++ = bufRxd[i];}cntRxd = 0;  //接收计数器清零return len;  //返回实际读取长度
}
/* 串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔 */
void UartRxMonitor(unsigned char ms)
{static unsigned char cntbkp = 0;static unsigned char idletmr = 0;if (cntRxd > 0)  //接收计数器大于零时,监控总线空闲时间{if (cntbkp != cntRxd)  //接收计数器改变,即刚接收到数据时,清零空闲计时{cntbkp = cntRxd;idletmr = 0;}else                   //接收计数器未改变,即总线空闲时,累积空闲时间{if (idletmr < 30)  //空闲计时小于30ms时,持续累加{idletmr += ms;if (idletmr >= 30)  //空闲时间达到30ms时,即判定为一帧接收完毕{flagFrame = 1;  //设置帧接收完成标志}}}}else{cntbkp = 0;}
}
/* 串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用 */
void UartDriver()
{unsigned char len;unsigned char pdata buf[40];if (flagFrame) //有命令到达时,读取处理该命令{flagFrame = 0;len = UartRead(buf, sizeof(buf)-2); //将接收到的命令读取到缓冲区中UartAction(buf, len);  //传递数据帧,调用动作执行函数}
}
/* 串口中断服务函数 */
void InterruptUART() interrupt 4
{if (RI)  //接收到新字节{RI = 0;  //清零接收中断标志位if (cntRxd < sizeof(bufRxd)) //接收缓冲区尚未用完时,{                            //保存接收字节,并递增计数器bufRxd[cntRxd++] = SBUF;}}if (TI)  //字节发送完毕{TI = 0;   //清零发送中断标志位flagTxd = 1;  //设置字节发送完成标志}
}

OLED.h


//******************************************************************************/
#include "reg51.h"
#ifndef __OLED_H
#define __OLED_H                 #define  u8 unsigned char
#define  u32 unsigned int
#define OLED_CMD  0 //写命令
#define OLED_DATA 1 //写数据
#define OLED_MODE 0sbit OLED_SCL=P2^1;//时钟 D0(SCLK?
sbit OLED_SDIN=P2^0;//D1(MOSI) 数据#define OLED_CS_Clr()  OLED_CS=0
#define OLED_CS_Set()  OLED_CS=1#define OLED_RST_Clr() OLED_RST=0
#define OLED_RST_Set() OLED_RST=1#define OLED_DC_Clr() OLED_DC=0
#define OLED_DC_Set() OLED_DC=1#define OLED_SCLK_Clr() OLED_SCL=0
#define OLED_SCLK_Set() OLED_SCL=1#define OLED_SDIN_Clr() OLED_SDIN=0
#define OLED_SDIN_Set() OLED_SDIN=1
//OLED模式设置
//0:4线串行模式
//1:并行8080模式#define SIZE 16          //16---------8
#define XLevelL     0x02
#define XLevelH     0x10
#define Max_Column  128
#define Max_Row     64
#define Brightness  0xFF
#define X_WIDTH     128
#define Y_WIDTH     64
//-----------------OLED端口定义----------------                        //OLED控制用函数
void OLED_WR_Byte(unsigned dat,unsigned cmd);
void OLED_Display_On(void);
void OLED_Display_Off(void);
void OLED_Init(void); //初始化
void OLED_Clear(void);//清空屏幕
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size);//显示字符
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);//显示整型
void OLED_ShowString(u8 x,u8 y, u8 *p,u8 Char_Size);//显示字符串
void OLED_Set_Pos(unsigned char x, unsigned char y);
void OLED_ShowCHinese(u8 x,u8 y,u8 no);
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]);
void IIC_Start();
void IIC_Stop();
void Write_IIC_Command(unsigned char IIC_Command);
void Write_IIC_Data(unsigned char IIC_Data);
void Write_IIC_Byte(unsigned char IIC_Byte);
void IIC_Wait_Ack();
#endif

OLED.c

#include "oled.h"
#include "oledfont.h"
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
void delay_ms(int ms)
{                         int i;int mmm;for(i=0;i<110;i++)for(mmm=ms;mmm>0;mmm--);
}
/**********************************************
//IIC Start
**********************************************/
void IIC_Start()
{OLED_SCLK_Set() ;OLED_SDIN_Set();OLED_SDIN_Clr();OLED_SCLK_Clr();
}/**********************************************
//IIC Stop
**********************************************/
void IIC_Stop()
{OLED_SCLK_Set() ;
//  OLED_SCLK_Clr();OLED_SDIN_Clr();OLED_SDIN_Set();}void IIC_Wait_Ack()
{OLED_SCLK_Set();OLED_SCLK_Clr();
}
/**********************************************
// IIC Write byte
**********************************************/void Write_IIC_Byte(unsigned char IIC_Byte)
{unsigned char i;unsigned char m,da;da=IIC_Byte;OLED_SCLK_Clr();for(i=0;i<8;i++)     {m=da;//   OLED_SCLK_Clr();m=m&0x80;if(m==0x80){OLED_SDIN_Set();}else OLED_SDIN_Clr();da=da<<1;OLED_SCLK_Set();OLED_SCLK_Clr();}
//........}
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{IIC_Start();Write_IIC_Byte(0x78);            //Slave address,SA0=0IIC_Wait_Ack(); Write_IIC_Byte(0x00);           //write commandIIC_Wait_Ack();  Write_IIC_Byte(IIC_Command); IIC_Wait_Ack();    IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{IIC_Start();Write_IIC_Byte(0x78);          //D/C#=0; R/W#=0IIC_Wait_Ack();   Write_IIC_Byte(0x40);           //write dataIIC_Wait_Ack(); Write_IIC_Byte(IIC_Data);IIC_Wait_Ack();    IIC_Stop();
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{if(cmd){Write_IIC_Data(dat);}else {Write_IIC_Command(dat);}}/********************************************
// fill_Picture
// ********************************************/
// void fill_picture(unsigned char fill_Data)
// {//  unsigned char m,n;
//  for(m=0;m<8;m++)
//  {//      OLED_WR_Byte(0xb0+m,0);        //page0-page1
//      OLED_WR_Byte(0x00,0);       //low column start address
//      OLED_WR_Byte(0x10,0);       //high column start address
//      for(n=0;n<128;n++)
//          {//              OLED_WR_Byte(fill_Data,1);
//          }
//  }
// }//坐标设置void OLED_Set_Pos(unsigned char x, unsigned char y)
{   OLED_WR_Byte(0xb0+y,OLED_CMD);OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ONOLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFFOLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{  u8 i,n;          for(i=0;i<8;i++)  {  OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); } //更新显示
}
void OLED_On(void)
{  u8 i,n;          for(i=0;i<8;i++)  {  OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA); } //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{       unsigned char c=0,i=0;    c=chr-' ';//得到偏移后的值          if(x>Max_Column-1){x=0;y=y+2;}if(Char_Size ==16){OLED_Set_Pos(x,y); for(i=0;i<8;i++)OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);OLED_Set_Pos(x,y+1);for(i=0;i<8;i++)OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);}
//      else {
//              OLED_Set_Pos(x,y);
//              for(i=0;i<6;i++)
//              OLED_WR_Byte(F6x8[c][i],OLED_DATA);
//
//          }
}
//m^n函数
u32 oled_pow(u8 m,u8 n)
{u32 result=1;  while(n--)result*=m;    return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式   0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{           u8 t,temp;u8 enshow=0;                        for(t=0;t<len;t++){temp=(num/oled_pow(10,len-t-1))%10;if(enshow==0&&t<(len-1)){if(temp==0){OLED_ShowChar(x+(size2/2)*t,y,' ',size2);continue;}else enshow=1; }OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); }
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{unsigned char j=0;while (chr[j]!='\0'){        OLED_ShowChar(x,y,chr[j],Char_Size);x+=8;if(x>120){x=0;y+=2;}j++;}
}
// //显示汉字
// void OLED_ShowCHinese(u8 x,u8 y,u8 no)
// {
//  u8 t,adder=0;
//  OLED_Set_Pos(x,y);
//     for(t=0;t<16;t++)
//      {//              OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
//              adder+=1;
//      }
//      OLED_Set_Pos(x,y+1);
//     for(t=0;t<16;t++)
//          {
//              OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
//              adder+=1;
//       }
// }
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
// void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
// {
//  unsigned int j=0;
//  unsigned char x,y;
//
//   if(y1%8==0) y=y1/8;
//   else y=y1/8+1;
//  for(y=y0;y<y1;y++)
//  {//      OLED_Set_Pos(x0,y);
//     for(x=x0;x<x1;x++)
//      {
//          OLED_WR_Byte(BMP[j++],OLED_DATA);
//      }
//  }
// } //初始化SSD1306
void OLED_Init(void)
{   OLED_WR_Byte(0xAE,OLED_CMD);//--display offOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address  OLED_WR_Byte(0xB0,OLED_CMD);//--set page addressOLED_WR_Byte(0x81,OLED_CMD); // contract controlOLED_WR_Byte(0xFF,OLED_CMD);//--128   OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverseOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 dutyOLED_WR_Byte(0xC8,OLED_CMD);//Com scan directionOLED_WR_Byte(0xD3,OLED_CMD);//-set display offsetOLED_WR_Byte(0x00,OLED_CMD);//OLED_WR_Byte(0xD5,OLED_CMD);//set osc divisionOLED_WR_Byte(0x80,OLED_CMD);//OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode offOLED_WR_Byte(0x05,OLED_CMD);//OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge PeriodOLED_WR_Byte(0xF1,OLED_CMD);//OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartionOLED_WR_Byte(0x12,OLED_CMD);//OLED_WR_Byte(0xDB,OLED_CMD);//set VcomhOLED_WR_Byte(0x30,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enableOLED_WR_Byte(0x14,OLED_CMD);//OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}  

效果
主机电脑发送,单片机接收并且在屏幕显示
03指令。

05指令。

通过主机发送,可以控制LED灯亮灭。
不同的Address,控制不同的LED灯。
address=0, P00=0,LED灯,ON,,OFF
这里的
0x01+0x05+0x00+地址+0x05/0x00(ON/OFF)+0x00+crc1+crc2

06指令。
在屏幕显示对应的数

51单片机,485,测试03/05/06相关推荐

  1. 51单片机距离测试软件,单片机超声波传感器测量距离

    一.设计要求 设计一个超声波测距器,可以应用于汽车倒车.建筑施工工地以及一些工业现场的位置监控,也可用于如液位.井深.管道长度的测量等场合.要求测量范围在0.10-3.00m,测量精度1cm,测量时与 ...

  2. c语言485通讯源程序,51单片机485通信实验C语言源代码实现

    #include #include sbit RS485=P3^7;   //定义485的使能脚 bit SendFlag; unsigned int ReData,SenData; /******* ...

  3. 51单片机距离测试软件,基于51单片机的红外线(强度)测距离

    一.原理:根据红外线强度随距离的增大而衰减的特性,只要找到距离与强度的关系,就可以得到要测量的距离了. 二.主要元器件清单 1. STC89C52 单片机 1 片 2. ICL 7109 A/D 转换 ...

  4. Modbus RTU 51单片机从机工程源码与昆仑通泰触摸屏测试工程文件。支持485和232串口通信

    Modbus RTU 51单片机从机工程源码与昆仑通泰触摸屏测试工程文件.支持485和232串口通信,该从机源码支持51系列和STC12系列单片机,支持功能码01,02,03,04,05,06,15, ...

  5. Modbus RTU 51单片机从机工程源码与昆仑通泰触摸屏测试工程文件

    Modbus RTU 51单片机从机工程源码与昆仑通泰触摸屏测试工程文件. 支持485和232串口通信,该从机源码支持51系列和STC12系列单片机,支持功能码01,02,03,04,05,06,15 ...

  6. Modbus RTU 51单片机从机源码与组态软件通信支持485和232串口通信,该从机源码可直接用于51系列和STC12系列单片机的

    Modbus RTU 51单片机从机源码与组态软件通信支持485和232串口通信,该从机源码可直接用于51系列和STC12系列单片机的,支持功能码01,02,03,04,05,06,0F,10等常用功 ...

  7. 51单片机基本刷屏测试实验_51单片机开发基础8——实时时钟实验

    8.1 实时时钟简介 RTC(Real Time Clock),是实时时钟的缩写,实时时钟是日常生活中应用最为广泛的功能.它为人们提供精确的实时时间,或者为电子系统提供精确的时间基准,目前实时时钟芯片 ...

  8. 51单片机基本刷屏测试实验_基于单片机的发动机振动速度、位移和加速度测量方法...

    Single-chip microcomputer-based measuring of engine vibration speed.displacement and acceleration Ab ...

  9. 51单片机与ESP8266轻松上手Onenet(二)--onenet AT指令测试

    本编内容如下: 1 准备工作 2 onenet产品创建 3 AT指令测试 4小结 51单片机与ESP8266快速上手物联网系列将一步步介绍89C51单片机将采集到的DS18B20的温度数据通过ESP8 ...

  10. 心率脉搏测试c语言算法,基于51单片机语音播报心率计脉搏测量仪设计(仿真源码+电路图+当时PaperPass16%查重论文)...

    一.本课题研究的主要内容.目的和意义 随着科技发展的不断提高,生命科学和信息科学的结合越来越紧密,出现了各种新颖的脉搏测量仪器,特别是电子脉搏仪的出现,使脉搏测量变得非常方便. 脉诊在我国已具有260 ...

最新文章

  1. splitcontainer如何设置两边一样打_墙洞加筋如何计算?
  2. Java新手,强烈不建议你用 a.equals(b) 判断对象相等!
  3. 提高ActiveMQ工作性能
  4. 数据结构上机测试1:顺序表的应用
  5. syslog数据接收并处理
  6. Swift -- 6.函数和闭包
  7. 解决github无法访问的问题
  8. codeforces 271A-C语言解题报告
  9. 硬件基础知识-- MOS管
  10. IDEA 快速搭建SpringBoot项目 提供GitHub源码地址
  11. 学习React之前你需要知道的的JavaScript基础知识
  12. 网络安全涉及到的知识积累(1)
  13. JavaScript实现移动端跟手轮播图
  14. elementui[el-table]表格全选操作以及翻页选中取消相关效果
  15. ad怎么导入cad的外形尺寸_AD10怎样精确导入CAD文件?
  16. 微软mysql官网_az mysql
  17. Hyperspectral Band Selection by Multitask Sparsity Puisuit
  18. 拖延、迷茫、无力感,就到此为止吧!
  19. 新南威尔士大学计算机博士英语要求,新南威尔士大学博士申请条件
  20. 【Electron-vue】创建桌面应用(12)- 修改electron窗口图标和桌面图标

热门文章

  1. python 面积计算器
  2. 智能衣橱控制系统的设计
  3. 【2013年总结】思维跌宕起伏,生命颠簸曲折的一年
  4. php 设置字符编码为utf-8
  5. JS处理文本框只能输入中文、英文、数字,防止SQL特殊字符注入(解决MAC中文输入法下会取得拼音的值的问题)
  6. cdn刷新api_【第1868期】闲话 CDN
  7. 音视频基本概念:码率、比特率
  8. 如何使用Excel制作标靶图
  9. 【C语言经典100题】乒乓球队的比赛
  10. .mat转.tif 用于arcgis裁剪遥感图像