C51智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结
目录
1.电机模块开发
1.1 让小车动起来
1.2 串口控制小车方向
1.3 如何进行小车PWM调速
1.4 PWM方式实现小车转向
2.循迹小车
2.1 循迹模块使用
2.2 循迹小车原理
2.3 循迹小车核心代码
3.跟随/避障小车
3.1 红外壁障模块分析编辑
3.2 跟随小车的原理
3.3 跟随小车开发和调试代码
3.4 超声波模块介绍
3.5 摇头测距小车开发和调试代码
4.测速小车
4.1 测速模块
4.2 测试原理和单位换算
4.3 定时器和中断实现测速开发和调试代码
4.4 小车速度显示在OLED屏
5.远程控制小车
5.1 蓝牙控制小车
5.2 蓝牙控制并测速小车
5.3 wifi控制测速小车
5.4 4g控制小车
6.语音控制小车
6.1语音模块配置:
6.2 语音控制小车开发和调试代码
1.电机模块开发
L9110s概述
接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,具体根据实际调试
IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转;
IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转;
IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转;
IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转;
1.1 让小车动起来
核心代码:
#include "reg52.h"
#include "intrins.h"sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;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 goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void main()
{while(1){goForward();Delay1000ms();Delay1000ms();goBack();Delay1000ms();Delay1000ms();goLeft();Delay1000ms();Delay1000ms();goRight();Delay1000ms();Delay1000ms();}
}
1.2 串口控制小车方向
- 串口分文件编程进行代码整合——具体过程看课程,主要考验C语言功底和代码调试能力,通过现象来改代码
- 接入蓝牙模块,通过蓝牙控制小车
- 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不 能实现),就能实现前进按键按下后小车一直往前走的功能
1.3 如何进行小车PWM调速
原理: 全速前进是LeftCon1A = 0; LeftCon1B = 1;完全停止是LeftCon1A = 0;LeftCon1B = 0;那么单位时 间内,比如20ms, 有15ms是全速前进,5ms是完全停止, 速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!
开发:借用PWM的舵机控制代码
核心代码:
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"extern char speed;void main()
{Time0Init();//UartInit();while(1){speed = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500usDelay1000ms();Delay1000ms();speed = 20;Delay1000ms();Delay1000ms();speed = 40;Delay1000ms();Delay1000ms();}
}//time.c
#include "motor.h"
#include "reg52.h"char speed;
char cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;//控制PWM波if(cnt < speed){//前进goForward();}else{//停止stop();}if(cnt == 40){//爆表40次,经过了20mscnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}
1.4 PWM方式实现小车转向
原理: 左轮定时器0调速,右轮定时器1调速,那么左转就是右轮速度大于左轮!
核心代码:
#include "motor.h"
#include "reg52.h"char speedLeft;
char cntLeft = 0;char speedRight;
char cntRight = 0;void Time1Init()
{//1. 配置定时器1工作模式位16位计时TMOD &= 0x0F;TMOD |= 0x1 << 4;//2. 给初值,定一个0.5出来TL1=0x33;TH1=0xFE;//3. 开始计时TR1 = 1;TF1 = 0;//4. 打开定时器1中断ET1 = 1;//5. 打开总中断EAEA = 1;
}void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time1Handler() interrupt 3
{cntRight++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL1=0x33;TH1=0xFE;//控制PWM波if(cntRight < speedRight){//右前进goForwardRight();}else{//停止stopRight();}if(cntRight == 40){//爆表40次,经过了20mscntRight = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}void Time0Handler() interrupt 1
{cntLeft++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;//控制PWM波if(cntLeft < speedLeft){//左前进goForwardLeft();}else{//停止stopLeft();}if(cntLeft == 40){//爆表40次,经过了20mscntLeft = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}
2.循迹小车
2.1 循迹模块使用
- TCRT5000传感器的红外发射二极管不断发射红外线
- 当发射出的红外线没有被反射回来或被反射回来但强度不够大时
- 红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
- 被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和
- 此时模块的输出端为低电平,指示二极管被点亮
- 总结就是一句话,没反射回来,D0输出高电平,灭灯
接线方式
- VCC:接电源正极(3-5V)
- GND:接电源负极 DO:TTL开关信号输出0、1
- AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)
2.2 循迹小车原理
由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致 循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常亮
总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯
2.3 循迹小车核心代码
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"
#include "reg52.h"
extern char speedLeft;
extern char speedRight;sbit leftSensor = P2^7;
sbit rightSensor = P2^6;void main()
{Time0Init();Time1Init();//UartInit();while(1){if(leftSensor == 0 && rightSensor == 0){speedLeft = 32;speedRight = 40;}if(leftSensor == 1 && rightSensor == 0){speedLeft = 12;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500usspeedRight = 40;}if(leftSensor == 0 && rightSensor == 1){speedLeft = 32;speedRight = 20;}if(leftSensor == 1 && rightSensor == 1){//停speedLeft = 0;speedRight = 0;}}
}//motor.c
#include "reg52.h"sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForwardLeft()
{LeftCon1A = 0;LeftCon1B = 1;
}void stopLeft()
{LeftCon1A = 0;LeftCon1B = 0;
}void goForwardRight()
{RightCon1A = 0;RightCon1B = 1;
}
void stopRight()
{RightCon1A = 0;RightCon1B = 0;
}void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//delay.c
#include "intrins.h"void Delay1000ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}//time.c
#include "motor.h"
#include "reg52.h"char speedLeft;
char cntLeft = 0;char speedRight;
char cntRight = 0;void Time1Init()
{//1. 配置定时器1工作模式位16位计时TMOD &= 0x0F;TMOD |= 0x1 << 4;//2. 给初值,定一个0.5出来TL1=0x33;TH1=0xFE;//3. 开始计时TR1 = 1;TF1 = 0;//4. 打开定时器1中断ET1 = 1;//5. 打开总中断EAEA = 1;
}void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time1Handler() interrupt 3
{cntRight++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL1=0x33;TH1=0xFE;//控制PWM波if(cntRight < speedRight){//右前进goForwardRight();}else{//停止stopRight();}if(cntRight == 40){//爆表40次,经过了20mscntRight = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}void Time0Handler() interrupt 1
{cntLeft++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;//控制PWM波if(cntLeft < speedLeft){//左前进goForwardLeft();}else{//停止stopLeft();}if(cntLeft == 40){//爆表40次,经过了20mscntLeft = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}
3.跟随/避障小车
3.1 红外壁障模块分析
原理和循迹是一样的,循迹红外观朝下,跟随朝前
3.2 跟随小车的原理
- 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
- 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
3.3 跟随小车开发和调试代码
//main.c
#include "motor.h"
#include "delay.h"
#include "reg52.h"//sbit leftSensor = P2^7;
//sbit rightSensor = P2^6;sbit leftSensor = P2^5;
sbit rightSensor = P2^4;void main()
{while(1){if(leftSensor == 0 && rightSensor == 0){goForward();}if(leftSensor == 1 && rightSensor == 0){goRight();}if(leftSensor == 0 && rightSensor == 1){goLeft();}if(leftSensor == 1 && rightSensor == 1){//停stop();}}
}//motor.c
#include "reg52.h"sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//delay.c#include "intrins.h"void Delay1000ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}
3.4 超声波模块介绍
使用超声波模块,型号:HC-SR04
- 怎么让它发送波 Trig ,给Trig端口至少10us的高电平
- 怎么知道它开始发了 Echo信号,由低电平跳转到高电平,表示开始发送波
- 怎么知道接收了返回波 Echo,由高电平跳转回低电平,表示波回来了
- 怎么算时间 Echo引脚维持高电平的时间! 波发出去的那一下,开始启动定时器 波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
- 怎么算距离 距离 = 速度 (340m/s)* 时间/2
时序图:
3.5 摇头测距小车开发和调试代码
//main.c
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "motor.h"#define MIDDLE 0
#define LEFT 1
#define RIGHT 2void main()
{char dir;double disMiddle;double disLeft;double disRight;Time0Init();Time1Init();//舵机的初始位置sgMiddle();Delay300ms();Delay300ms();dir = MIDDLE;while(1){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();}}}
}//hc04.c
#include "reg52.h"
#include "delay.h"sbit Trig = P2^3;
sbit Echo = P2^2;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)* 时间/2return (time * 0.017);
}//delay.c
#include "intrins.h"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 Delay300ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 3;j = 26;k = 223;do{do{while (--k);} while (--j);} while (--i);
}void Delay150ms() //@11.0592MHz
{unsigned char i, j, k;i = 2;j = 13;k = 237;do{do{while (--k);} while (--j);} while (--i);
}void Delay450ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 4;j = 39;k = 209;do{do{while (--k);} while (--j);} while (--i);
}//sg90.c
#include "reg52.h"
#include "delay.h"sbit sg90_con = P1^1;int jd;
int cnt = 0;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. 打开总中断EAEA = 1;
}void sgMiddle()
{//中间位置jd = 3; //90度 1.5ms高电平cnt = 0;
}void sgLeft()
{//左边位置jd = 5; //135度 1.5ms高电平cnt = 0;
}void sgRight()
{//右边位置jd = 1; //0度cnt = 0;
}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次,经过了20mscnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1ssg90_con = 1;}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}
4.测速小车
4.1 测速模块
- 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
- 有遮挡,输出高电平;无遮挡,输出低电平
- 接线 :VCC 接电源正极3.3-5V
- GND 接电源负极 DO TTL开关信号输出
- AO 此模块不起作用
4.2 测试原理和单位换算
- 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
- 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
- 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
- 假设一秒有80脉冲,那么就是80cm/s
4.3 定时器和中断实现测速开发和调试代码
测试数据通过串口发送到上位机
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24]; //主程序发送速度数据的字符串缓冲区void Ex0Init()
{EX0 = 1;//允许中断//EA = 1;在串口初始化函数中已经打开了总中断IT0 = 1;//外部中断的下降沿触发
}void main()
{Time0Init();//定时器0初始化UartInit();//串口相关初始化//外部中断初始化Ex0Init();while(1){if(signal){//定时器1s到点,把signal置一,主程序发送速度sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cmSendString(speedMes);//速度发出去signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一}}
}void speedHandler() interrupt 0 //外部中断处理函数
{speedCnt++;//码盘转动了一个格子
}//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12sfr AUXR = 0x8E;
char buffer[SIZE];void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器EA = 1;//开启总中断ES = 1;//开启串口中断
}void SendByte(char mydata)
{SBUF = mydata;while(!TI);TI = 0;
}void SendString(char *str)
{while(*str != '\0'){SendByte(*str);str++;}
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'M'){i = 0;}buffer[i++] = tmp;//灯控指令if(buffer[0] == 'M'){switch(buffer[1]){case '1':goForward();break;case '2':goBack();break;case '3':goLeft();break;case '4':goRight();break;default:stop();break;}}if(i == 12) {memset(buffer, '\0', SIZE);i = 0;}}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//time.c
#include "motor.h"
#include "reg52.h"extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,经过了1ssignal = 1;cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s//计算小车的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零}}
4.4 小车速度显示在OLED屏
使用oled模块
//main.c
#include "reg52.h"
#include "intrins.h"
#include "Oled.h"void main()
{//1. OLED初始化Oled_Init();Oled_Clear();Oled_Show_Str(2,2,"speed:35cm/s");while(1);
}//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"sbit scl = P1^2;
sbit sda = P1^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 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Init(void){Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}void Oled_Clear()
{unsigned char i,j; //-128 --- 127for(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);}}
}void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2unsigned int i;Oled_Write_Cmd(0xb0+(row*2-2)); //page 0Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}Oled_Write_Cmd(0xb0+(row*2-1)); //page 1Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //highfor(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}
}/******************************************************************************/
// 函数名称:Oled_Show_Char
// 输入参数:oledChar
// 输出参数:无
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}
5.远程控制小车
5.1 蓝牙控制小车
- 使用蓝牙模块,串口透传
- 蓝牙模块,又叫做蓝牙串口模块
串口透传技术:
- 透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿 佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。
- 以太网,蓝牙,Zigbee, GPRS 等模块玩法一样,对嵌入式程序员来说,不需要关心通讯模块内部数据 及协议栈工作原理,只要通过串口编程获得数据即可
代码实现:
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"void main()
{UartInit();while(1){stop();}
}//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
#include "delay.h"
sbit D5 = P3^7;
#define SIZE 12sfr AUXR = 0x8E;
char buffer[SIZE];void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器EA = 1;//开启总中断ES = 1;//开启串口中断
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'M'){i = 0;}buffer[i++] = tmp;//灯控指令if(buffer[0] == 'M'){switch(buffer[1]){case '1':goForward();Delay10ms();break;case '2':goBack();Delay10ms();break;case '3':goLeft();Delay10ms();break;case '4':goRight();Delay10ms();break;default:stop();break;}}if(i == 12) {memset(buffer, '\0', SIZE);i = 0;}}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//delay.c#include "intrins.h"void Delay10ms() //@11.0592MHz
{unsigned char i, j;i = 18;j = 235;do{while (--j);} while (--i);
}void Delay1000ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}
5.2 蓝牙控制并测速小车
原理:运用上面讲到的蓝牙模块和测速模块
代码实现:
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"
#include "Oled.h"sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24]; //主程序发送速度数据的字符串缓冲区void Ex0Init()
{EX0 = 1;//允许中断//EA = 1;在串口初始化函数中已经打开了总中断IT0 = 1;//外部中断的下降沿触发
}void main()
{Time0Init();//定时器0初始化UartInit();//串口相关初始化//外部中断初始化Ex0Init();Oled_Init();Oled_Clear();while(1){if(signal){//定时器1s到点,把signal置一,主程序发送速度sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cmSendString(speedMes);//速度发出去signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一}Oled_Show_Str(2,2,speedMes);}
}void speedHandler() interrupt 0 //外部中断处理函数
{speedCnt++;//码盘转动了一个格子
}//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12sfr AUXR = 0x8E;
char buffer[SIZE];void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器EA = 1;//开启总中断ES = 1;//开启串口中断
}void SendByte(char mydata)
{SBUF = mydata;while(!TI);TI = 0;
}void SendString(char *str)
{while(*str != '\0'){SendByte(*str);str++;}
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'M'){i = 0;}buffer[i++] = tmp;//灯控指令if(buffer[0] == 'M'){switch(buffer[1]){case '1':goForward();break;case '2':goBack();break;case '3':goLeft();break;case '4':goRight();break;default:stop();break;}}if(i == 12) {memset(buffer, '\0', SIZE);i = 0;}}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//time.c
#include "motor.h"
#include "reg52.h"extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,经过了1ssignal = 1;cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s//计算小车的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零}}//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"sbit scl = P1^2;
sbit sda = P1^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 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Init(void){Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}void Oled_Clear()
{unsigned char i,j; //-128 --- 127for(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);}}
}void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2unsigned int i;Oled_Write_Cmd(0xb0+(row*2-2)); //page 0Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}Oled_Write_Cmd(0xb0+(row*2-1)); //page 1Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //highfor(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}
}/******************************************************************************/
// 函数名称:Oled_Show_Char
// 输入参数:oledChar
// 输出参数:无
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}
5.3 wifi控制测速小车
- Wifi模块-ESP-01s
- 蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计
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指令以回车作为结 尾,响应或上报以回车换行为结尾。
代码实现:
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"
#include "Oled.h"
#include "esp8266.h"sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24]; //主程序发送速度数据的字符串缓冲区
//发送数据
char FSSJ[] = "AT+CIPSEND=0,5\r\n";void Ex0Init()
{EX0 = 1;//允许中断//EA = 1;在串口初始化函数中已经打开了总中断IT0 = 1;//外部中断的下降沿触发
}void main()
{Time0Init();//定时器0初始化UartInit();//串口相关初始化Delay1000ms();//给espwifi模块上电时间initWifi_AP(); //初始化wifi工作在ap模式waitConnect(); //等待客户端的连接//外部中断初始化Ex0Init();Oled_Init();Oled_Clear();while(1){if(signal){//定时器1s到点,把signal置一,主程序发送速度SendString(FSSJ);Delay1000ms();sprintf(speedMes,"%dcms",speed);//串口数据的字符串拼装,speed是格子,每个格子1cmSendString(speedMes);//速度发出去signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一}Oled_Show_Str(2,2,speedMes);}
}void speedHandler() interrupt 0 //外部中断处理函数
{speedCnt++;//码盘转动了一个格子
}//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12sfr AUXR = 0x8E;
char buffer[SIZE];extern char AT_OK_Flag; //OK返回值的标志位
extern char Client_Connect_Flag;void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器EA = 1;//开启总中断ES = 1;//开启串口中断
}void SendByte(char mydata)
{SBUF = mydata;while(!TI);TI = 0;
}void SendString(char *str)
{while(*str != '\0'){SendByte(*str);str++;}
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'M' || tmp == 'O' || tmp == '0'){i = 0;}buffer[i++] = tmp;//连接服务器等OK返回值指令的判断if(buffer[0] == 'O' && buffer[1] == 'K'){AT_OK_Flag = 1;memset(buffer, '\0', SIZE);}if(buffer[0] == '0' && buffer[2] == 'C'){Client_Connect_Flag = 1;memset(buffer, '\0', SIZE);}//灯控指令if(buffer[0] == 'M'){switch(buffer[1]){case '1':goForward();break;case '2':goBack();break;case '3':goLeft();break;case '4':goRight();break;default:stop();break;}}if(i == 12) {memset(buffer, '\0', SIZE);i = 0;}}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//time.c
#include "motor.h"
#include "reg52.h"extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,经过了1ssignal = 1;cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s//计算小车的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零}}//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"sbit scl = P1^2;
sbit sda = P1^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 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Init(void){Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}void Oled_Clear()
{unsigned char i,j; //-128 --- 127for(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);}}
}void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2unsigned int i;Oled_Write_Cmd(0xb0+(row*2-2)); //page 0Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}Oled_Write_Cmd(0xb0+(row*2-1)); //page 1Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //highfor(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}
}/******************************************************************************/
// 函数名称:Oled_Show_Char
// 输入参数:oledChar
// 输出参数:无
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}//esp8266.c
#include "uart.h"//1 工作在路由模式
code char LYMO[] = "AT+CWMODE=2\r\n";
//2 使能多链接
code char DLJ[] = "AT+CIPMUX=1\r\n";
//3 建立TCPServer
code char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333 char AT_OK_Flag = 0; //OK返回值的标志位
char Client_Connect_Flag = 0;void initWifi_AP()
{SendString(LYMO);while(!AT_OK_Flag);AT_OK_Flag = 0;SendString(DLJ);while(!AT_OK_Flag);AT_OK_Flag = 0;
}void waitConnect()
{SendString(JLFW);while(!Client_Connect_Flag);AT_OK_Flag = 0;
}//delay.c
#include "intrins.h"void Delay1000ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}
5.4 4g控制小车
原理:运用EC03-DNC4G通信模块
模块介绍:
- 基于串口AT指令的开发方式
- 有两种工作模式,默认是透传模式,通过其他方式进入AT指令模式
- 注意插卡不要出错,下图红色位置为SIM卡状态灯,亮才是正常
代码不做修改,直接基于蓝牙小车整合, 4g模块只要做好外网透传就可以了
6.语音控制小车
6.1语音模块配置:
使用SU-03T / LD3320
具体介绍看我之前写过的博客:https://blog.csdn.net/m0_74712453/article/details/13171085
6.2 语音控制小车开发和调试代码
代码示例:
//main.c
#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 3sbit 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();}}
}//hc04.c
#include "reg52.h"
#include "delay.h"sbit Trig = P2^3;
sbit Echo = P2^2;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)* 时间/2return (time * 0.017);
}//delay.c
#include "intrins.h"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 Delay300ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 3;j = 26;k = 223;do{do{while (--k);} while (--j);} while (--i);
}void Delay150ms() //@11.0592MHz
{unsigned char i, j, k;i = 2;j = 13;k = 237;do{do{while (--k);} while (--j);} while (--i);
}void Delay450ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 4;j = 39;k = 209;do{do{while (--k);} while (--j);} while (--i);
}//sg90.c
#include "reg52.h"
#include "delay.h"sbit sg90_con = P1^1;int jd;
int cnt = 0;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. 打开总中断EAEA = 1;
}void sgMiddle()
{//中间位置jd = 3; //90度 1.5ms高电平cnt = 0;
}void sgLeft()
{//左边位置jd = 5; //135度 1.5ms高电平cnt = 0;
}void sgRight()
{//右边位置jd = 1; //0度cnt = 0;
}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次,经过了20mscnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1ssg90_con = 1;}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"sbit scl = P1^2;
sbit sda = P1^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 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Init(void){Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}void Oled_Clear()
{unsigned char i,j; //-128 --- 127for(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);}}
}void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2unsigned int i;Oled_Write_Cmd(0xb0+(row*2-2)); //page 0Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}Oled_Write_Cmd(0xb0+(row*2-1)); //page 1Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //highfor(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}
}/******************************************************************************/
// 函数名称:Oled_Show_Char
// 输入参数:oledChar
// 输出参数:无
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}
相关文章:
C51智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结
目录 1.电机模块开发 1.1 让小车动起来 1.2 串口控制小车方向 1.3 如何进行小车PWM调速 1.4 PWM方式实现小车转向 2.循迹小车 2.1 循迹模块使用 2.2 循迹小车原理 2.3 循迹小车核心代码 3.跟随/避障小车 3.1 红外壁障模块分析编辑 3.2 跟随小车的原理 3.3 跟随小…...
回归预测 | MATLAB实现PCA-BP主成分降维结合BP神经网络多输入单输出回归预测
回归预测 | MATLAB实现PCA-BP主成分降维结合BP神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现PCA-BP主成分降维结合BP神经网络多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现PCA-BP主成分降维算法结合BP神经网络多输入单输出回…...
Kubernetes(k8s)部署高可用多主多从的Redis集群
Kubernetes部署高可用多主多从的Redis集群 环境准备准备Kubernetes准备存储类 部署redis准备一个命名空间命令创建yaml文件创建(推荐) 准备redis配置文件准备部署statefulset的资源清单文件执行文件完成部署初始化集群 环境准备 准备Kubernetes 首先你…...
算法专题:前缀和
文章目录 Acwing:前缀和示例2845.统计趣味子数组的数目思路容易理解的写法:前缀和两层循环存在问题:超时 优化写法:两数之和思路,转换为哈希表 前缀和,就是求数组中某一段的所有元素的和。 求子数组中某一…...
bs4库爬取天气预报
Python不仅用于网站开发,数据分析,图像处理,也常用于爬虫技术方向,最近学习了解下,爬虫技术入门一般先使用bs4库,爬取天气预报简单尝试下。 第一步:首先选定目标网站地址 网上查询,…...
l8-d8 TCP并发实现
一、TCP多进程并发 1.地址快速重用 先退出服务端,后退出客户端,则服务端会出现以下错误: 地址仍在使用中 解决方法: /*地址快速重用*/ int flag1,len sizeof (int); if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &a…...
编写中间件以用于 Express 应用程序
概述 中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。 中间件函数可以执行以下任务: 执行任何代码。对请求和响应对象进行更改。结束请求/响应循环。调用堆…...
【2023年数学建模国赛】D题解题思路
2023年数学建模国赛D题解题思路 为了解决问题1、问题2和问题3,我们可以采用动态规划方法来制定生产计划,考虑了不确定性因素和多种可能情况的预案集。首先,我们需要定义一些变量和符号: T T T:总的养殖周期࿰…...
python爬虫之正则表达式学习
网络安全离不开脚本和工具的开发,python很多又需要正则表达式。 这是一个很好的学习正则表达式的项目 https://github.com/ziishaned/learn-regex/blob/master/translations/README-cn.md 基本匹配 正则表达式其实就是在执行搜索时的格式,它由一些字…...
智慧能源方案:TSINGSEE青犀AI算法中台在能源行业的应用
一、方案背景 互联网、物联网、人工智能等新一代信息技术引领新一轮产业革命,加快能源革命步伐。尤其是随着人工智能技术的不断发展,AI智能检测与识别技术在能源行业的应用也越来越广泛。与此同时,国家出台多项政策,将智慧能源纳…...
达梦数据库awr报告收集
1、找出快照点snap_id与时间的对应关系 SYS.WRM$_SNAPSHOT表中记录了快照点snap_id与时间的对应关系 例如如下语句可以得出2023-09-04这一天各个时间点对应的快照点snap_id select snap_id,end_interval_time from SYS.WRM$_SNAPSHOT where end_interval_time between to…...
c语言练习43:深入理解strcmp
深入理解strcmp strcmp的主要功能是用来比较两个字符串 模拟实现strcmp 比较两个字符串对应位置上的大小 按字典序进行比较 例如: 输入:abc abc 输出:0 输入:abc ab 输出:>0的数 输入:ab abc …...
NUC980webServer开发
目录 1.RTL8189FTV驱动移植 2.wifi配置工具hostapd移植 1.openssl-1.0.2r交叉编译 2.libnl-3.2.25.tar.gz交叉编译 3.hostapd-2.9.tar.gz交叉编译 4.移植相关工具到开发板 1.RTL8189FTV驱动移植 1. 把驱动文件源码放在linux源码的drivers/net/wireless/realtek/rtlwifi/目录…...
驱动开发--day2
实现三盏灯的控制,编写应用程序测试 head.h #ifndef __HEAD_H__ #define __HEAD_H__#define LED1_MODER 0X50006000 #define LED1_ODR 0X50006014 #define LED1_RCC 0X50000A28#define LED2_MODER 0X50007000 #define LED2_ODR 0X50007014#endif mychrdev.c #inc…...
用户促活留存新方式——在APP中嵌入小游戏
随着APP同类产品的不断出现,APP开发者们面临着激烈的竞争,很多APP下载后被新的APP取代,获客成本越来越高。同时开发者还会面临用户粘性差、忠诚度低、用完即走、留存困难,商业化价值被大大缩减。 在APP中植入小游戏来提高用户活跃…...
MySQL 8.0.34(x64)安装笔记
一、背景 从MySQL 5.6到5.7,再到8.0,版本的跳跃不可谓不大。安装、配置的差别也不可谓不大,特此备忘。 二、过程 (1)获取MySQL 8.0社区版(MySQL Community Server) 从 官网 字样 “MySQL …...
物流供应商实现供应链自动化的3种方法
当前影响供应链的全球性问题(如新冠肺炎疫情)正在推动许多物流供应商重新评估和简化其流程。运输协调中的摩擦只会加剧供应商无法控制的现有延误和风险。值得庆幸的是,供应链专业人员可以通过端到端的供应链自动化消除延迟,简化与合作伙伴的沟通…...
Mysql更新时间列只改日期为指定日期不更改时间
场景 Mysql分表后同结构不同名称表之间复制数据以及Update语句只更新日期加减不更改时间: Mysql分表后同结构不同名称表之间复制数据以及Update语句只更新日期加减不更改时间_霸道流氓气质的博客-CSDN博客 上面通过如下方式实现日期列增加指定天数。 UPDATE bus…...
实时测试工具 Visual Studio 扩展 NCrunch 4.18 Crack
NCrunch Visual Studio 扩展 .NET 的终极实时测试工具 在编码时查看实时测试结果和内联指标。 下载v4.18 发布于 2023 年 7 月 17 日 跳过视频至: 代码覆盖率 指标 分布式处理 配置 发动机模式 Visual Studio 自动并发测试 NCrunch 是一个完全自动化的测试扩展&a…...
Neo4j 基本语法
一、基本语法 1、新建节点 (1)基本语法: () 代表节点 示例: CREATE (u:User {uid:970939424 }) // 节点类型为User,属性值为uid970939424CREATE (u:Round {rid:7194842697444819113 }) // 节点类型为Rou…...
docker常见面试题
1.什么是docker docker是一个容器化平台,类似于一个集装箱,集装箱与集装箱之间互不影响,docker平台就是一个软件集装箱平台,我们可以构建应用程序,将其所有的依赖打包到一个容器中,然后就很方便的可以在其…...
静态路由:配置和使用详解
文章目录 一、静态路由的配置和使用详解1. 配置要点1.1 点到点接口配置1.2 以太网接口配置 2. 默认路由3. 静态路由的配置命令4. 静态路由实现路由备份和负载分担 二、静态路由的优先级和比较1. 静态路由的优先级设置2. 静态路由与动态路由的比较2.1 静态路由优缺点2.2 动态路由…...
玩转Mysql系列 - 第15篇:详解视图
这是Mysql系列第15篇。 环境:mysql5.7.25,cmd命令中进行演示。 需求背景 电商公司领导说:给我统计一下:当月订单总金额、订单量、男女订单占比等信息,我们啪啦啪啦写了一堆很复杂的sql,然后发给领导。 …...
0065__git fetch, git pull, git merge, git rebase
git fetch, git pull, git merge, git rebase_git pull和merge_送你一朵小莲花的博客-CSDN博客...
AJAX学习笔记4解决乱码问题
AJAX学习笔记3练习_biubiubiu0706的博客-CSDN博客 在Tomcat10来说,AJAX GET或者POST接收响应都不存在乱码问题 对于Tomcat9来说 前端测试代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>测试A…...
【23种设计模式】享元模式【⭐】
个人主页:金鳞踏雨 个人简介:大家好,我是金鳞,一个初出茅庐的Java小白 目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作 我的博客&am…...
语音信号的仿真原理
利用MATLAB对语音信号进行分析和处理,采集语音信号后,利用MATLAB软件 平台进行频谱分析;并对所采集的语音信号加入干扰噪声,对加入噪声的信号进行频 谱分析,设计合适的滤波器滤除噪声,恢复原信号。语音信…...
VLDB 2023 | CDSBen: 字节跳动 veDB 数据库存储系统性能测试模型
背景 随着业务爆炸式增长与云原生技术的日渐成熟,大量云原生分布式数据库产品如雨后春笋般涌现,其中一部分主打 OLTP 场景的分布式数据库强调的是从计算-存储分离架构获得弹性收益;对于业界各种计算-存储分离架构的数据库而言,怎么…...
crontab的配置参数和基础使用教程
crontab基本格式 crontab文件的基本格式如下: * * * * * command 这5个*代表: 第一个* :分钟(0-59)第二个* :小时(0-23)第三个* :一个月中的第几天(1-31)第四个* :月份(1-12)第五个* :一周中的第几天(0-6,其中0代表星期天) command代表要执行的命令。 crontab常用时间设置…...
基于Python开发的玛丽大冒险小游戏(源码+可执行程序exe文件+程序配置说明书+程序使用说明书)
一、项目简介 本项目是一套基于Python开发的玛丽冒险小游戏程序,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含:项目源码、项目文档等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试…...
怎么介绍自己做的静态网站/关键词排名seo
本文主要是介绍使用prerender-spa-plugin插件在针对前端代码进行预渲染。 预渲染(SSG)和服务端(SSR)渲染有一定的区别,大家想要了解的话可以看:https://segmentfault.com/a/1190000023469150。 背景 因为…...
个人相册网站建设报告/软文写作实训总结
CreateTime--2017年10月20日09:35:49 Author:Marydon js如何获取ip地址? 参考链接:http://www.cnblogs.com/lyWebstrat/p/6824022.html js是不可以直接获取ip通过调用API 的调用方法 实现方法:通过回调查看ip 腾讯的IP地址API接口地址&#…...
广州白云区公司注册/seo搜索引擎优化是做什么的
aptitude aptHow to find the history of updated packages by apt-get or aptitude?如何通过apt -get或aptitude查找更新软件包的历史记录? The history is in log files of dpkg and apt:历史记录在dpkg和apt的日志文件中: /var/log/dpkg.log /var/…...
网站建设风险是什么意思/企业所得税优惠政策
众里寻他千百度,蓦然回首,那人却在灯火阑珊处。--《青玉案元夕》要学会看crash崩溃和报告一个应用程序并不总会一直运行的很好,它总会有出现crash崩溃的情况。如果在应用程序中接入了一些第三方的crash收集工具或者自建crash收集报告平台的话…...
浮梁网站建设/海外seo是什么
nacos zookeeper数据迁移 官网:https://nacos.io/zh-cn/docs/nacos-sync-use.html nacos-sync:https://github.com/nacos-group/nacos-sync nacos-sync nacos-sync 支持迁移的数据中心 nacos <> nacos:支持0.8到1.0及以上版本的nacos数…...
周口网站制作公司哪家好/河南百度关键词优化排名软件
package.json 非官方字段集合 package.json 官方字段请参考 https://docs.npmjs.com/files/package.json。下面介绍的是非官方字段,也就是各种工具定义的相关字段。 1. yarn 相关字段 yarn: 类似 npm 的依赖管理工具,但 yarn 缓存了每个下载过的包&am…...