C51单片机开发笔记

定时器

C51中的定时器和计数器是同一个硬件电路支持的,通过寄存器配置不同,就可以将他当做定时器 或者计数器使用。

确切的说,定时器和计数器区别是致使他们背后的计数存储器加1的信号不同。当配置为定时器使用时,每经过1个机器周期,计数存储器的值就加1。而当配置为计数器时,每来一个负跳变信号 (信号从P3.4 或者P3.5引脚输入),就加1,以此达到计数的目的。

标准C51有2个定时器/计数器:T0和T1。他们的使用方法一致。C52相比C51多了一个T2

定时器的本质原理: 每经过一个机器周期,寄存器就加1

什么是时钟周期

时钟周期也称为振荡周期,定义为时钟频率的倒数。时钟周期是计算机中最基本的、最小的时间单 位。在一个时钟周期内,CPU仅完成一个最基本的动作。时钟周期是一个时间的量。更小的时钟周期就意味着更高的工作频率

什么是机器周期

机器周期也称为CPU周期。在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶 段(如取指、译码、执行等),每一阶段完成一个基本操作。完成一个基本操作所需要的时间称为 机器周期。一般情况下,一个机器周期由若干个时钟周期组成

定时器编程

  • 在哪里加1,最大计数时间,也就是爆表了能计算多长

​ 在TH0/1和TL0/1寄存器中加1,默认是从0开始数数,最多能数65536下,累计计时71ms

  • 如何算出10ms定时器的初值

​ 就不让他从0开始数数,10ms需要数9216下,你让他从65536-9126=56320(16进制表示为 0xDC00)开始数数

​ 这样TL0=0x00;TH0=0xDC

  • 怎么知道爆表

​ TCON寄存器的bit5(TF0)能表示爆表:当爆表的时候,硬件会修改bit5(TF0)位上面的数据,改成 1(置1),如果不用中断,我们代码清零

  • 怎么开始计时

​ TCON寄存器的bit4,通过编程让这个位为1的时候,开始计时,相当于按下了闹钟

  • 定时器使用是有很多种模式的

​ 定时器模式寄存器:TMOD来选择定时器模式,选择工作方式1,TMOD的bit0 bit1配置成0 1 :16 的定时器功能

中断

感应开关垃圾桶

#include "reg52.h"
sbit D5 = P3 ^ 7; // 根据原理图(电路图),设备变量led1指向P3组IO口的第7口
sbit D6 = P3 ^ 6; // 根据原理图(电路图),设备变量led2指向P3组IO口的第6口
sbit SW1 = P2 ^ 1;
sbit Trig = P1 ^ 5;
sbit Echo = P1 ^ 6;
sbit sg90_con = P1 ^ 1;
sbit vibrate = P3 ^ 2;
sbit beep = P2 ^ 0;
char jd;
char jd_bak;
char cnt = 0;
char mark_vibrate = 0;
void Delay150ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 2;
j = 13;
k = 237;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i)
;
}
void Time0Init()
{
// 1. 配置定时器0工作模式位16位计时
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01;
// 2. 给初值,定一个0.5出来
TL0 = 0x33;
TH0 = 0xFE;
// 3. 开始计时
TR0 = 1;
TF0 = 0;
// 4. 打开定时器0中断
ET0 = 1;
// 5. 打开总中断EA
EA = 1;
}
void Time1Init()
{
TMOD &= 0x0F; // 设置定时器模式
TMOD |= 0x10;
TH1 = 0;
TL1 = 0;
// 设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}
void startHC()
{
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
double get_distance()
{
double time;
// 定时器数据清零,以便下一次测距
TH1 = 0;
TL1 = 0;
// 1. Trig ,给Trig端口至少10us的高电平
startHC();
// 2. echo由低电平跳转到高电平,表示开始发送波
while (Echo == 0)
;
// 波发出去的那一下,开始启动定时器
TR1 = 1;
// 3. 由高电平跳转回低电平,表示波回来了
while (Echo == 1)
;
// 波回来的那一下,我们开始停止定时器
TR1 = 0;
// 4. 计算出中间经过多少时间
time = (TH1 * 256 + TL1) * 1.085; // us为单位
// 5. 距离 = 速度 (340m/s)* 时间/2
return (time * 0.017);
}
void openStatusLight()
{
D5 = 0;
D6 = 1;
}
void closeStatusLight()
{
D5 = 1;
D6 = 0;
}
void initSG90_0()
{
jd = 1; // 初始角度是0度,0.5ms,溢出1就是0.5,高电平
cnt = 0;
sg90_con = 1; // 一开始从高电平开始
}
void openDusbin()
{
char n;
jd = 3; // 90度 1.5ms高电平
// 舵机开盖
if (jd_bak != jd)
{
cnt = 0;
beep = 0;
for (n = 0; n < 2; n++)
Delay150ms();
beep = 1;
Delay2000ms();
}
jd_bak = jd;
}
void closeDusbin()
{
// 关盖
jd = 1; // 0度
jd_bak = jd;
cnt = 0;
Delay150ms();
}
void EX0_Init()
{
// 打开外部中断
EX0 = 1;
// 低电平触发
IT0 = 0;
}
void main()
{
double dis;
Time0Init();
Time1Init();
EX0_Init();
// 舵机的初始位置
initSG90_0();
while (1)
{
// 超声波测距
dis = get_distance();
if (dis < 10 || SW1 == 0 || mark_vibrate == 1)
{ // 如果小于10厘米,或者sw1
按键被按下
// 开盖,灯状态,D5亮
openStatusLight();
openDusbin();
mark_vibrate = 0;
}
else
{
// 关盖,灯状态,D5灭
closeStatusLight();
closeDusbin();
}
}
}
void Time0Handler() interrupt 1
{ cnt++; // 统计爆表的次数. cnt=1的时候,报表了1
// 重新给初值
TL0 = 0x33;
TH0 = 0xFE;
// 控制PWM波
if (cnt < jd)
{
sg90_con = 1;
}
else
{
sg90_con = 0;
}
if (cnt == 40)
{ // 爆表40次,经过了20ms
cnt = 0; // 当100次表示1s,重新让cnt从0开始,计算下一次的1s
sg90_con = 1;
}
}
void Ex0_Handler() interrupt 0
{
mark_vibrate = 1;
}

串口通信

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方 式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简 单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成 本,特别适用于远距离通信,但传送速度较慢

  • 是设备间接线通信的一种方式
  • 数据一位一位地顺序传送
  • 双向通信,全双工
  • 传送速度相对较慢

异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。 UART包含TTL电平的串口和RS232电平的串口

字符 'a' 是如何从单片机上传到PC的

a的ASSII码是97,16进制就是0x61, 二进制是01010001,这个8位就是数据位

串口工作模式1,一帧数据有10位,起始位(0),数据位,停止位(1)

那么a的一帧数据就是 0 1000 1010 1 起始位,a的低位到高位,停止位

  • 除了速度要求,还要有数据格式,双方 暗号 对上了再发数据,所以有起始位,和停止位 的概念

  • 一个字节有8位,比如字母‘a’的ASSII码是十进制97,二进制是 0110 0001 ,一次从地位到高位发 送,接收也是

Wifi模块-ESP8266

AT指令

AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个 字符的长度(包括最后的空字符)。每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结 尾,响应或上报以回车换行为结尾。

初始配置和验证

ESP-8266 出厂波特率正常是115200, 注意:AT指令,控制类都要加回车,数据传输时不加回车

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3 ^ 7;
sbit D6 = P3 ^ 6;
char buffer[SIZE];
code char LJWL[] = "AT+CWJAP=\"TP-LINK_3E30\",\"18650711783\"\r\n"; // 入网指令
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.0.113\",8880\r\n"; // 连接服务器
指令
char TCMS[] = "AT+CIPMODE=1\r\n"; // 透传指令
char SJCS[] = "AT+CIPSEND\r\n"; // 数据传输开始指令
char RESET[] = "AT+RST\r\n"; // 重启模块指令
char AT_OK_Flag = 0; // OK返回值的标志位
char AT_Connect_Net_Flag = 0; // WIFI GOT IP返回值的标志位
void UartInit(void) // 9600bps@11.0592MHz
{
AUXR = 0x01;
SCON = 0x50; // 配置串口工作方式1,REN使能接收
TMOD &= 0xF0;
TMOD |= 0x20; // 定时器1工作方式位8位自动重装
TH1 = 0xFD;
TL1 = 0xFD; // 9600波特率的初值
TR1 = 1; // 启动定时器
EA = 1; // 开启总中断
ES = 1; // 开启串口中断
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void sendByte(char data_msg)
{
SBUF = data_msg;
while (!TI);
TI = 0;
}
void sendString(char *str)
{
while (*str != '\0')
{
sendByte(*str);
str++;
}
}
void main()
{
int mark = 0;
D5 = D6 = 1; // 灭状态灯
// 配置C51串口的通信方式
UartInit();
Delay1000ms(); // 给espwifi模块上电时间
// 发送联网AT指令并等待成功
sendString(LJWL);
while (!AT_Connect_Net_Flag)
;
while (!AT_OK_Flag)
;
AT_OK_Flag = 0;
// 发送连服务器指令并等待成功
sendString(LJFWQ);
while (!AT_OK_Flag)
;
AT_OK_Flag = 0;
// 发送透传模式指令并等待成功
sendString(TCMS);
while (!AT_OK_Flag)
;
AT_OK_Flag = 0;
// 发送数据传输指令并等待成功
sendString(SJCS);
while (!AT_OK_Flag)
;
if (AT_Connect_Net_Flag)
{
D5 = 0; // 点亮D5,代表入网成功
}
if (AT_OK_Flag)
{
D6 = 0; // 点亮D6,代表连接服务器并打开透传模式成功
}
while (1)
{
Delay1000ms();
// “心跳包”
sendString("zjn shuai\r\n");
}
} void Uart_Handler() interrupt 4
{
static int i = 0; // 静态变量,被初始化一次
char tmp;
if (RI) // 中断处理函数中,对于接收中断的响应
{
RI = 0; // 清除接收中断标志位
tmp = SBUF;
if (tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == 'F')
{
i = 0;
}
buffer[i++] = tmp;
// 入网成功的判断依据WIFI GOT IP
if (buffer[0] == 'W' && buffer[5] == 'G')
{
AT_Connect_Net_Flag = 1;
memset(buffer, '\0', SIZE);
}
// 连接服务器等OK返回值指令的判断
if (buffer[0] == 'O' && buffer[1] == 'K')
{
AT_OK_Flag = 1;
memset(buffer, '\0', SIZE);
}
// 联网失败出现FAIL字样捕获
if (buffer[0] == 'F' && buffer[1] == 'A')
{
for (i = 0; i < 5; i++)
{
D5 = 0;
Delay1000ms();
D5 = 1;
Delay1000ms();
}
sendString(RESET);
memset(buffer, '\0', SIZE);
}
// 灯控指令
if (buffer[0] == 'L' && buffer[2] == '1')
{
D5 = 0; // 点亮D5
memset(buffer, '\0', SIZE);
}
if (buffer[0] == 'L' && buffer[2] == '0')
{
D5 = 1; // 熄灭D5
memset(buffer, '\0', SIZE);
}
if (i == 12)
i = 0;
}
}

IIC协议

特点

  • 简单性和有效性。

由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件

  • 多主控(multimastering)

其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

构成

IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控 器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线 的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。

IIC总线在传输数据的过程中一共有三种类型信号,分别为:起始信号、结束信号和应答信号。 //起始位,停止位,数据位,速度

  • 应答信号

发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字 节;

应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功

  • 数据发送的时序

sbit scl = P0 ^ 1;
sbit sda = P0 ^ 3;
void IIC_Start()
{
sda = 1;
scl = 1;
_nop_();
sda = 0;
_nop_();
}
void IIC_Stop()
{
sda = 0;
scl = 1;
_nop_();
sda = 1;
_nop_();
}
char IIC_ACK()
{
char flag;
sda = 1; // 就在时钟脉冲9期间释放数据线
_nop_();
scl = 1;
_nop_();
flag = sda;
_nop_();
scl = 0;
_nop_();
return flag;
}
void IIC_Send_Byte(char dataSend)
{
int i;
for (i = 0; i < 8; i++)
{
scl = 0; // scl拉低,让sda做好数据准备
sda = dataSend & 0x80; // 1000 0000获得dataSend的最高位,给sda
_nop_(); // 发送数据建立时间
scl = 1; // scl拉高开始发送
_nop_(); // 数据发送时间
scl = 0; // 发送完毕拉低
_nop_(); //
dataSend = dataSend << 1;
}
}

OLED写命令

void Oled_Write_Cmd(char dataCmd)
{
// 1. start()
IIC_Start();
//
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x00);
// 5. ACK
IIC_ACK();
// 6. 写入指令/数据
IIC_Send_Byte(dataCmd);
// 7. ACK
IIC_ACK();
// 8. STOP
IIC_Stop();
}
void Oled_Write_Data(char dataData)
{
// 1. start()
IIC_Start();
//
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x00);
// 5. ACK
IIC_ACK();
/// 6. 写入指令/数据
IIC_Send_Byte(dataData);
// 7. ACK
IIC_ACK();
// 8. STOP
IIC_Stop();
}

OLED的寻址模式

如何显示一个点? 有三种,分别位页地址模式,水平地址模式和垂直地址模式,可以通过一下表格进行配置

内存管理

页地址模式

水平地址模式

垂直地址模式

列地址选择

!

如果写入0x08(b00001000)会显示什么呢 一个字节负责一个Page的一列显示

#include "reg52.h"
#include "intrins.h"
sbit scl = P0 ^ 1;
sbit sda = P0 ^ 3;
void IIC_Start()
{
scl = 0;
sda = 1;
scl = 1;
_nop_();
sda = 0;
_nop_();
}
void IIC_Stop()
{
scl = 0;
sda = 0;
scl = 1;
_nop_();
sda = 1;
_nop_();
}
char IIC_ACK()
{
char flag;
sda = 1; // 就在时钟脉冲9期间释放数据线
_nop_();
scl = 1;
_nop_();
flag = sda;
_nop_();
scl = 0;
_nop_();
return flag;
}
void IIC_Send_Byte(char dataSend)
{
int i;
for (i = 0; i < 8; i++)
{
scl = 0; // scl拉低,让sda做好数据准备
sda = dataSend & 0x80; // 1000 0000获得dataSend的最高位,给sda
_nop_(); // 发送数据建立时间
scl = 1; // scl拉高开始发送
_nop_(); // 数据发送时间
scl = 0; // 发送完毕拉低
_nop_(); //
dataSend = dataSend << 1;
}
}
void Oled_Write_Cmd(char dataCmd)
{
// 1. start()
IIC_Start();
//
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x00);
// 5. ACK
IIC_ACK();
// 6. 写入指令/数据
IIC_Send_Byte(dataCmd);
// 7. ACK
IIC_ACK();
// 8. STOP
IIC_Stop();
}
void Oled_Write_Data(char dataData)
{
// 1. start()
IIC_Start();
//
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x40);
// 5. ACK
IIC_ACK();
/// 6. 写入指令/数据
IIC_Send_Byte(dataData);
// 7. ACK
IIC_ACK();
// 8. STOP
IIC_Stop();
}
void Oled_Init(void)
{
Oled_Write_Cmd(0xAE); //--display off
Oled_Write_Cmd(0x00); //---set low column address
Oled_Write_Cmd(0x10); //---set high column address
Oled_Write_Cmd(0x40); //--set start line address
Oled_Write_Cmd(0xB0); //--set page address
Oled_Write_Cmd(0x81); // contract control
Oled_Write_Cmd(0xFF); //--128
Oled_Write_Cmd(0xA1); // set segment remap
Oled_Write_Cmd(0xA6); //--normal / reverse
Oled_Write_Cmd(0xA8); //--set multiplex ratio(1 to 64)
Oled_Write_Cmd(0x3F); //--1/32 duty
Oled_Write_Cmd(0xC8); // Com scan direction
Oled_Write_Cmd(0xD3); //-set display offset
Oled_Write_Cmd(0x00); //
Oled_Write_Cmd(0xD5); // set osc division
Oled_Write_Cmd(0x80); //
Oled_Write_Cmd(0xD8); // set area color mode off
Oled_Write_Cmd(0x05); //
Oled_Write_Cmd(0xD9); // Set Pre-Charge Period
Oled_Write_Cmd(0xF1); //
Oled_Write_Cmd(0xDA); // set com pin configuartion
Oled_Write_Cmd(0x12); //
Oled_Write_Cmd(0xDB); // set Vcomh
Oled_Write_Cmd(0x30); //
Oled_Write_Cmd(0x8D); // set charge pump enable
Oled_Write_Cmd(0x14); //
Oled_Write_Cmd(0xAF); //--turn on oled panel
}
void Oled_Clear()
{
unsigned char i, j; //-128 --- 127
for (i = 0; i < 8; i++)
{
Oled_Write_Cmd(0xB0 + i); // page0--page7
// 每个page从0列
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
// 0到127列,依次写入0,每写入数据,列地址自动偏移
for (j = 0; j < 128; j++)
{
Oled_Write_Data(0);
}
}
}
/*-- 文字: A --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
char A1[8] = {0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00};
char A2[8] = {0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20};
/*-- 文字: 上 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
char s1[16] =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00};
char s2[16] =
{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00};
/*-- 文字: 官 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char g1[16] =
{0x10, 0x0C, 0x04, 0xE4, 0x24, 0x24, 0x25, 0x26, 0x24, 0x24, 0x24, 0xE4, 0x04, 0x14, 0x0C, 0x00};
code char g2[16] =
{0x00, 0x00, 0x00, 0xFF, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0xF8, 0x00, 0x00, 0x00};
/*-- 文字: 可 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char k1[16] =
{0x00, 0x02, 0x02, 0xF2, 0x12, 0x12, 0x12, 0xF2, 0x02, 0x02, 0x02, 0xFE, 0x02, 0x02, 0x02, 0x00};
code char k2[16] =
{0x00, 0x00, 0x00, 0x0F, 0x04, 0x04, 0x04, 0x0F, 0x00, 0x40, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00};
/*-- 文字: 编 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char b1[16] =
{0x20, 0x30, 0xAC, 0x63, 0x30, 0x00, 0xFC, 0x24, 0x25, 0x26, 0x24, 0x24, 0x24, 0x3C, 0x00, 0x00};
code char b2[16] =
{0x22, 0x67, 0x22, 0x12, 0x52, 0x38, 0x07, 0xFF, 0x09, 0x7F, 0x09, 0x3F, 0x89, 0xFF, 0x00, 0x00};
/*-- 文字: 程 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char c1[16] =
{0x24, 0x24, 0xA4, 0xFE, 0x23, 0x22, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x00, 0x00};
code char c2[16] =
{0x08, 0x06, 0x01, 0xFF, 0x01, 0x06, 0x40, 0x49, 0x49, 0x49, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00};
void main()
{
unsigned char i;
// 1. OLED初始化
Oled_Init();
// 2. 选择一个位置
// 2.1 确认页寻址模式
Oled_Write_Cmd(0x20);
Oled_Write_Cmd(0x02);
Oled_Clear();
// 2.2 选择PAGE0 1011 0000
// 0xB0
Oled_Write_Cmd(0xB0);
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
for (i = 0; i < 16; i++)
{
Oled_Write_Data(s1[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(g1[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(k1[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(b1[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(c1[i]);
}
Oled_Write_Cmd(0xB1);
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
for (i = 0; i < 16; i++)
{
Oled_Write_Data(s2[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(g2[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(k2[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(b2[i]);
}
for (i = 0; i < 16; i++)
{
Oled_Write_Data(c2[i]);
}
while (1)
;
}

小车项目

#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "Oled.h"
#include "motor.h"
#define MIDDLE 0
#define LEFT 1
#define RIGHT 2
#define BZ 1
#define XJ 2
#define GS 3
sbit A25 = P1 ^ 5;
sbit A26 = P1 ^ 6;
sbit A27 = P1 ^ 7;
sbit leftSensorX = P2 ^ 7;
sbit rightSensorX = P2 ^ 6;
sbit leftSensorG = P2 ^ 5;
sbit rightSensorG = P2 ^ 4;
char dir;
double disMiddle;
double disLeft;
double disRight;
void xunjiMode()
{
if (leftSensorX == 0 && rightSensorX == 0)
{
goForward();
}
if (leftSensorX == 1 && rightSensorX == 0)
{
goLeft();
}
if (leftSensorX == 0 && rightSensorX == 1)
{
goRight();
}
if (leftSensorX == 1 && rightSensorX == 1)
{
// 停
stop();
}
}
void gensuiMode()
{
if (leftSensorG == 0 && rightSensorG == 0)
{
goForward();
}
if (leftSensorG == 1 && rightSensorG == 0)
{
goRight();
}
if (leftSensorG == 0 && rightSensorG == 1)
{
goLeft();
}
if (leftSensorG == 1 && rightSensorG == 1)
{
// 停
stop();
}
}
void bizhangMode()
{
if (dir != MIDDLE)
{
sgMiddle();
dir = MIDDLE;
Delay300ms();
}
disMiddle = get_distance();
if (disMiddle > 35)
{
// 前进
goForward();
}
else if (disMiddle < 10)
{
goBack();
}
else
{
// 停止
stop();
// 测左边距离
sgLeft();
Delay300ms();
disLeft = get_distance();
sgMiddle();
Delay300ms();
sgRight();
dir = RIGHT;
Delay300ms();
disRight = get_distance();
if (disLeft < disRight)
{
goRight();
Delay150ms();
stop();
}
if (disRight < disLeft)
{
goLeft();
Delay150ms();
stop();
}
}
}
void main()
{
int mark = 0;
Time0Init();
Time1Init();
// 舵机的初始位置
sgMiddle();
Delay300ms();
Delay300ms(); dir = MIDDLE;
Oled_Init();
Oled_Clear();
Oled_Show_Str(2, 2, "-----Ready----");
while (1)
{
// 满足寻迹模式的条件
if (A25 == 0 && A26 == 1 && A27 == 1)
{
if (mark != XJ)
{
Oled_Clear();
Oled_Show_Str(2, 2, "-----XunJi----");
}
mark = XJ;
xunjiMode();
}
// 满足跟随模式的条件
if (A25 == 1 && A26 == 0 && A27 == 1)
{
if (mark != GS)
{
Oled_Clear();
Oled_Show_Str(2, 2, "-----GenSui----");
}
mark = GS;
gensuiMode();
}
// 满足避障模式的条件
if (A25 == 1 && A26 == 1 && A27 == 0)
{
if (mark != BZ)
{
Oled_Clear();
Oled_Show_Str(2, 2, "-----BiZhang----");
}
mark = BZ;
bizhangMode();
}
}
}

C51单片机开发的更多相关文章

  1. 单片机开发——01工欲善其事必先利其器(Keil软件安装破解)

        本文是博主<单片机开发>博客第一篇文章,主要讲述51单片机编程软件Keil uVision4的安装及破解过程. 1. Keil uVision4安装包文件      PATH:链接 ...

  2. C51单片机头文件和启动文件

    STARTUP.A51//启动文件. 清理RAM.设置堆栈等.即执行完start.a51后跳转到.c文件的main函数 <reg51.h>  //特殊寄存器的字节地址和位地址,sfr定义字 ...

  3. Linux上进行单片机开发

    linux上可以使用sdcc进行单片机开发 ubuntu使用 apt-get install sdcc 即可安装. 附一个比较通用的Makefile PRJ := test SRC := $(wild ...

  4. 单片机开发——03工欲善其事必先利其器(AD软件安装破解)

    在单片机开发中,有了Keil的程序编程,Protues的模拟仿真,那么问题来了,怎么去进行电路图设计以及硬件调试呢?此刻就必须引入本文的Altium Designer (下文简称AD)安装与破解.在硬 ...

  5. 单片机开发——02工欲善其事必先利其器(Proteus软件安装破解)

    在单片机开发工程中,博主经常通过模拟软件Proteus进行模拟仿真,将编译生成的"HEX"文件下载在单片机芯片中,然后进行后期的debug工作,当模拟仿真完成之后,进行硬件测试部分 ...

  6. C51单片机_day_01(定时器和中断系统)

                c51单片机 51单片机是控制电路系统的开关,当然芯片就是51芯片,现在随着科技的发展,也是出了很多,功能更多,更全的芯片. 51是用c语言做为程序编程的语言 ——我对基本基础 ...

  7. C51单片机中data、idata、xdata、pdata的区别

    C51单片机中data.idata.xdata.pdata的区别 data: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小. idata: 固定指前 ...

  8. 树莓派、 Arduino 、传统单片机开发板该如何选择?

    几十年前的电子爱好者,最喜欢的就是电烙铁.面包板和收音机:十几年前,出现了单片机,于是玩具就成了电烙铁.面包板和单片机:到了2015年,贴片技术的不断普及,让面包板不再那么有用武之地,经济的发展也让现 ...

  9. 一种基于C51单片机的非抢占式的操作系统架构

    摘 要:从Keil C51的内存空间管理方式入手,着重讨论实时操作系统在任务调度时的重入问题,分析一些解决重入的基本方式与方法:分析实时操作系统任务调度的占先性,提出非占先的任务调度是能更适合于Kei ...

  10. [学习笔记]15个QA让你快速入门51单片机开发

    一.C语言相关 Q1:sbit与sfr代表是什么?有什么作用? Q2:#define OSC_FREQ  22118400L这句宏命令里的“L”是什么意思? Q3:我粘贴了别人的代码,怎么发现没有un ...

随机推荐

  1. 可视化容器管理工具-portainer.io使用

    续docker日常使用指南 背景 当我们开始使用docker后,我们的机器上镜像和容器会越来越多,或者有时候我们有多台开发机的时候,单纯使用命令行去管理镜像和容器就变得麻烦了,这时,我们就可以选择一些 ...

  2. 用写代码的方式画图-试下PlantUML吧

    1 序言 所谓一图胜千言,大家平日在工作中编写文档时,往往都需要画各种图来表达中心思想,比如流程图.时序图.UML 图,很多人选择使用 Axure .PrecessOn.Diagrams(darw.i ...

  3. 通用权限系统-Dozer对象转换

    Dozer对象转换 介绍 Dozer 是一个 Java Bean 到 Java Bean 的映射器,它可以递归地将数据从一个对象复制到另一个.通常情况下,这些Java Beans将是不同的复杂类型. ...

  4. Flutter ListView 不满屏 无法滚动

    场景: flutter ListView 在做上拉加载的时候,未list添加controller后,数据不满屏,无法滚动 解决: 未listview 设置: physics: const Always ...

  5. 近期uniapp使用与总结

    弟弟是个uniapp小白,有什么问题欢迎指正. 吃什么饭对于有选择困难的我来说是个大问题,所以想做个根据自己输入的食物随机分配每餐吃的东西,然后就准备用uniapp做这样一个软件,主要是uniapp打 ...

  6. 一文了解io包中的discard类型

    1. 引言 io.discard是Go语言标准库提供一个结构体类型,其在丢弃不需要的数据场景下非常好用.本文我们将从io.discard 类型的基本定义出发,讲述其基本使用和实现原理,接着简单描述 i ...

  7. BUUCTF-MISC-LSB(stegsolve的一种妙用)

    题目已知是LSB隐写 丢入stegsolve,点 > ,可以看见Red plane 0,Green plane 0,Blue plane 0上边好像有东西 点analyse->data e ...

  8. Unity UGUI的LayoutElement(布局元素)组件的介绍及使用

    Unity UGUI的LayoutElement(布局元素)组件的介绍及使用 1. 什么是LayoutElement组件? LayoutElement是Unity UGUI中的一个布局元素组件,用于控 ...

  9. 万字长文浅析配置对MySQL服务器的影响

    有很多的服务器选项会影响这MySQL服务器的性能,比如内存中临时表的大小.排序缓冲区等.有些针对特定存储引擎(如InnoDB)的选项,也会对查询优化很有用. 调整服务器的配置从某种程度来说是一个影响全 ...

  10. AWVS14破解docker一键安装

    先上个图 2021最新版 1.使用docker查看是否有awvs:    [root@hadoop-01 awvs13-linux]# docker search awvs    NAME       ...