C51单片机开发

C51单片机开发笔记

定时器#

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

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

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

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

什么是时钟周期

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

什么是机器周期

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

定时器编程

image

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

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

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

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

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

image

  • 怎么知道爆表

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

  • 怎么开始计时

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

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

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

中断#

image

感应开关垃圾桶#

#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都有唯一的地址。

image

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

image

  • 应答信号

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

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

image

  • 数据发送的时序

image

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的寻址模式#

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

内存管理

image

image

页地址模式

image

水平地址模式

image

垂直地址模式

image

列地址选择

!image

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

image

#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();
        }
    }
}

作者:keep--fighting

出处:https://www.cnblogs.com/keep--fighting/p/17726896.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   ⭐⭐-fighting⭐⭐  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示