有做瀏覽單的網(wǎng)站/百度小說風(fēng)云榜2022
目錄
1.電機(jī)模塊開發(fā)
1.1 讓小車動(dòng)起來
1.2 串口控制小車方向
1.3 如何進(jìn)行小車PWM調(diào)速
1.4 PWM方式實(shí)現(xiàn)小車轉(zhuǎn)向
2.循跡小車?
2.1 循跡模塊使用
2.2 循跡小車原理
2.3 循跡小車核心代碼
3.跟隨/避障小車
3.1 紅外壁障模塊分析?編輯
3.2 跟隨小車的原理
3.3 跟隨小車開發(fā)和調(diào)試代碼
3.4 超聲波模塊介紹
3.5 搖頭測距小車開發(fā)和調(diào)試代碼
4.測速小車
4.1 測速模塊
4.2 測試原理和單位換算
4.3 定時(shí)器和中斷實(shí)現(xiàn)測速開發(fā)和調(diào)試代碼
4.4 小車速度顯示在OLED屏
5.遠(yuǎn)程控制小車
5.1 藍(lán)牙控制小車
5.2 藍(lán)牙控制并測速小車
5.3 wifi控制測速小車
5.4 4g控制小車
6.語音控制小車
6.1語音模塊配置:
6.2 語音控制小車開發(fā)和調(diào)試代碼
1.電機(jī)模塊開發(fā)
L9110s概述
接通VCC,GND 模塊電源指示燈亮, 以下資料來源官方,具體根據(jù)實(shí)際調(diào)試
IA1輸入高電平,IA1輸入低電平,【OA1 OB1】電機(jī)正轉(zhuǎn);
IA1輸入低電平,IA1輸入高電平,【OA1 OB1】電機(jī)反轉(zhuǎn);
IA2輸入高電平,IA2輸入低電平,【OA2 OB2】電機(jī)正轉(zhuǎn);
IA2輸入低電平,IA2輸入高電平,【OA2 OB2】電機(jī)反轉(zhuǎn);
1.1 讓小車動(dòng)起來
核心代碼:
#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 串口控制小車方向
- 串口分文件編程進(jìn)行代碼整合——具體過程看課程,主要考驗(yàn)C語言功底和代碼調(diào)試能力,通過現(xiàn)象來改代碼
- 接入藍(lán)牙模塊,通過藍(lán)牙控制小車
- 添加點(diǎn)動(dòng)控制,如果APP支持按下一直發(fā)數(shù)據(jù),松開就停止發(fā)數(shù)據(jù)(藍(lán)牙調(diào)試助手的自定義按鍵不 能實(shí)現(xiàn)),就能實(shí)現(xiàn)前進(jìn)按鍵按下后小車一直往前走的功能
1.3 如何進(jìn)行小車PWM調(diào)速
原理: 全速前進(jìn)是LeftCon1A = 0; LeftCon1B = 1;完全停止是LeftCon1A = 0;LeftCon1B = 0;那么單位時(shí) 間內(nèi),比如20ms, 有15ms是全速前進(jìn),5ms是完全停止, 速度就會(huì)比5ms全速前進(jìn),15ms完全停止獲得的功率多,相應(yīng)的速度更快!
開發(fā):借用PWM的舵機(jī)控制代碼
核心代碼:
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"extern char speed;void main()
{Time0Init();//UartInit();while(1){speed = 10;//10份單位時(shí)間全速運(yùn)行,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. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD = 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器0中斷ET0 = 1;//5. 打開總中斷EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;//控制PWM波if(cnt < speed){//前進(jìn)goForward();}else{//停止stop();}if(cnt == 40){//爆表40次,經(jīng)過了20mscnt = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s}}
1.4 PWM方式實(shí)現(xiàn)小車轉(zhuǎn)向
原理: 左輪定時(shí)器0調(diào)速,右輪定時(shí)器1調(diào)速,那么左轉(zhuǎn)就是右輪速度大于左輪!
核心代碼:
#include "motor.h"
#include "reg52.h"char speedLeft;
char cntLeft = 0;char speedRight;
char cntRight = 0;void Time1Init()
{//1. 配置定時(shí)器1工作模式位16位計(jì)時(shí)TMOD &= 0x0F;TMOD |= 0x1 << 4;//2. 給初值,定一個(gè)0.5出來TL1=0x33;TH1=0xFE;//3. 開始計(jì)時(shí)TR1 = 1;TF1 = 0;//4. 打開定時(shí)器1中斷ET1 = 1;//5. 打開總中斷EAEA = 1;
}void Time0Init()
{//1. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD = 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器0中斷ET0 = 1;//5. 打開總中斷EAEA = 1;
}void Time1Handler() interrupt 3
{cntRight++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL1=0x33;TH1=0xFE;//控制PWM波if(cntRight < speedRight){//右前進(jìn)goForwardRight();}else{//停止stopRight();}if(cntRight == 40){//爆表40次,經(jīng)過了20mscntRight = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s}}void Time0Handler() interrupt 1
{cntLeft++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;//控制PWM波if(cntLeft < speedLeft){//左前進(jìn)goForwardLeft();}else{//停止stopLeft();}if(cntLeft == 40){//爆表40次,經(jīng)過了20mscntLeft = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s}}
2.循跡小車?
2.1 循跡模塊使用
- TCRT5000傳感器的紅外發(fā)射二極管不斷發(fā)射紅外線
- 當(dāng)發(fā)射出的紅外線沒有被反射回來或被反射回來但強(qiáng)度不夠大時(shí)
- 紅外接收管一直處于關(guān)斷狀態(tài),此時(shí)模塊的輸出端為高電平,指示二極管一直處于熄滅狀態(tài)
- 被檢測物體出現(xiàn)在檢測范圍內(nèi)時(shí),紅外線被反射回來且強(qiáng)度足夠大,紅外接收管飽和
- 此時(shí)模塊的輸出端為低電平,指示二極管被點(diǎn)亮
- 總結(jié)就是一句話,沒反射回來,D0輸出高電平,滅燈
接線方式
- VCC:接電源正極(3-5V)
- GND:接電源負(fù)極 DO:TTL開關(guān)信號(hào)輸出0、1
- AO:模擬信號(hào)輸出(不同距離輸出不同的電壓,此腳一般可以不接)
2.2 循跡小車原理
由于黑色具有較強(qiáng)的吸收能力,當(dāng)循跡模塊發(fā)射的紅外線照射到黑線時(shí),紅外線將會(huì)被黑線吸收,導(dǎo)致 循跡模塊上光敏三極管處于關(guān)閉狀態(tài),此時(shí)模塊上一個(gè)LED熄滅。在沒有檢測到黑線時(shí),模塊上兩個(gè)LED常亮
總結(jié)就是一句話,有感應(yīng)到黑線,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份單位時(shí)間全速運(yùn)行,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. 配置定時(shí)器1工作模式位16位計(jì)時(shí)TMOD &= 0x0F;TMOD |= 0x1 << 4;//2. 給初值,定一個(gè)0.5出來TL1=0x33;TH1=0xFE;//3. 開始計(jì)時(shí)TR1 = 1;TF1 = 0;//4. 打開定時(shí)器1中斷ET1 = 1;//5. 打開總中斷EAEA = 1;
}void Time0Init()
{//1. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD = 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器0中斷ET0 = 1;//5. 打開總中斷EAEA = 1;
}void Time1Handler() interrupt 3
{cntRight++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL1=0x33;TH1=0xFE;//控制PWM波if(cntRight < speedRight){//右前進(jìn)goForwardRight();}else{//停止stopRight();}if(cntRight == 40){//爆表40次,經(jīng)過了20mscntRight = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s}}void Time0Handler() interrupt 1
{cntLeft++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;//控制PWM波if(cntLeft < speedLeft){//左前進(jìn)goForwardLeft();}else{//停止stopLeft();}if(cntLeft == 40){//爆表40次,經(jīng)過了20mscntLeft = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s}}
3.跟隨/避障小車
3.1 紅外壁障模塊分析
原理和循跡是一樣的,循跡紅外觀朝下,跟隨朝前
3.2 跟隨小車的原理
- 左邊跟隨模塊能返回紅外,輸出低電平,右邊不能返回,輸出高電平,說明物體在左邊,需要左轉(zhuǎn)
- 右邊跟隨模塊能返回紅外,輸出低電平,左邊不能返回,輸出高電平,說明物體在右邊,需要右轉(zhuǎn)
3.3 跟隨小車開發(fā)和調(diào)試代碼
//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 超聲波模塊介紹
使用超聲波模塊,型號(hào):HC-SR04
- 怎么讓它發(fā)送波 Trig ,給Trig端口至少10us的高電平
- 怎么知道它開始發(fā)了 Echo信號(hào),由低電平跳轉(zhuǎn)到高電平,表示開始發(fā)送波
- 怎么知道接收了返回波 Echo,由高電平跳轉(zhuǎn)回低電平,表示波回來了
- 怎么算時(shí)間 Echo引腳維持高電平的時(shí)間! 波發(fā)出去的那一下,開始啟動(dòng)定時(shí)器 波回來的拿一下,我們開始停止定時(shí)器,計(jì)算出中間經(jīng)過多少時(shí)間
- 怎么算距離 距離 = 速度 (340m/s)* 時(shí)間/2
時(shí)序圖:
3.5 搖頭測距小車開發(fā)和調(diào)試代碼
//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();//舵機(jī)的初始位置sgMiddle();Delay300ms();Delay300ms();dir = MIDDLE;while(1){if(dir != MIDDLE){sgMiddle();dir = MIDDLE;Delay300ms();}disMiddle = get_distance();if(disMiddle > 35){//前進(jìn)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; //設(shè)置定時(shí)器模式TMOD |= 0x10;TH1 = 0;TL1 = 0;//設(shè)置定時(shí)器0工作模式1,初始值設(shè)定0開始數(shù)數(shù),不著急啟動(dòng)定時(shí)器
}void startHC()
{Trig = 0;Trig = 1;Delay10us();Trig = 0;
}double get_distance()
{double time;//定時(shí)器數(shù)據(jù)清零,以便下一次測距TH1 = 0;TL1 = 0;//1. Trig ,給Trig端口至少10us的高電平startHC();//2. echo由低電平跳轉(zhuǎn)到高電平,表示開始發(fā)送波while(Echo == 0);//波發(fā)出去的那一下,開始啟動(dòng)定時(shí)器TR1 = 1;//3. 由高電平跳轉(zhuǎn)回低電平,表示波回來了while(Echo == 1);//波回來的那一下,我們開始停止定時(shí)器TR1 = 0;//4. 計(jì)算出中間經(jīng)過多少時(shí)間time = (TH1 * 256 + TL1)*1.085;//us為單位//5. 距離 = 速度 (340m/s)* 時(shí)間/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. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD &= 0xF0; //設(shè)置定時(shí)器模式TMOD |= 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器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++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;//控制PWM波if(cnt < jd){sg90_con = 1;}else{sg90_con = 0;}if(cnt == 40){//爆表40次,經(jīng)過了20mscnt = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的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 測速模塊
- 用途:廣泛用于電機(jī)轉(zhuǎn)速檢測,脈沖計(jì)數(shù),位置限位等。
- 有遮擋,輸出高電平;無遮擋,輸出低電平
- 接線 :VCC 接電源正極3.3-5V
- GND 接電源負(fù)極 DO TTL開關(guān)信號(hào)輸出
- AO 此模塊不起作用
4.2 測試原理和單位換算
- 輪子走一圈,經(jīng)過一個(gè)周長,C = 2x3.14x半徑= 3.14 x 直徑(6.5cm)
- 對應(yīng)的碼盤也轉(zhuǎn)了一圈,碼盤有20個(gè)格子,每經(jīng)過一個(gè)格子,會(huì)遮擋(高電平)和不遮擋(低電平), 那么一個(gè)脈沖就是走了 3.14 * 6.5 cm /20 = 1.0205CM
- 定時(shí)器可以設(shè)計(jì)成一秒,統(tǒng)計(jì)脈沖數(shù),一個(gè)脈沖就是1cm
- 假設(shè)一秒有80脈沖,那么就是80cm/s
4.3 定時(shí)器和中斷實(shí)現(xiàn)測速開發(fā)和調(diào)試代碼
測試數(shù)據(jù)通過串口發(fā)送到上位機(jī)
//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; //統(tǒng)計(jì)格子,脈沖次數(shù)
extern unsigned int speed;//速度
extern char signal; //主程序發(fā)速度數(shù)據(jù)的通知
char speedMes[24]; //主程序發(fā)送速度數(shù)據(jù)的字符串緩沖區(qū)void Ex0Init()
{EX0 = 1;//允許中斷//EA = 1;在串口初始化函數(shù)中已經(jīng)打開了總中斷IT0 = 1;//外部中斷的下降沿觸發(fā)
}void main()
{Time0Init();//定時(shí)器0初始化UartInit();//串口相關(guān)初始化//外部中斷初始化Ex0Init();while(1){if(signal){//定時(shí)器1s到點(diǎn),把signal置一,主程序發(fā)送速度sprintf(speedMes,"speed:%d cm/s",speed);//串口數(shù)據(jù)的字符串拼裝,speed是格子,每個(gè)格子1cmSendString(speedMes);//速度發(fā)出去signal = 0;//清0speed,下次由定時(shí)器1s后的中斷處理中再置一}}
}void speedHandler() interrupt 0 //外部中斷處理函數(shù)
{speedCnt++;//碼盤轉(zhuǎn)動(dòng)了一個(gè)格子
}//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;//定時(shí)器1工作方式位8位自動(dòng)重裝TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//啟動(dòng)定時(shí)器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;//靜態(tài)變量,被初始化一次char tmp;if(RI)//中斷處理函數(shù)中,對于接收中斷的響應(yīng){RI = 0;//清除接收中斷標(biāo)志位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. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD = 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器0中斷ET0 = 1;//5. 打開總中斷EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,經(jīng)過了1ssignal = 1;cnt = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s//計(jì)算小車的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt個(gè)格子,就能算出這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;//就在時(shí)鐘脈沖9期間釋放數(shù)據(jù)線_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做好數(shù)據(jù)準(zhǔn)備sda = dataSend & 0x80;//1000 0000獲得dataSend的最高位,給sda_nop_();//發(fā)送數(shù)據(jù)建立時(shí)間scl = 1;//scl拉高開始發(fā)送_nop_();//數(shù)據(jù)發(fā)送時(shí)間scl = 0;//發(fā)送完畢拉低_nop_();//dataSend = dataSend << 1;}
}void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 寫入指令/數(shù)據(jù)IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 寫入指令/數(shù)據(jù)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//每個(gè)page從0列Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);//0到127列,依次寫入0,每寫入數(shù)據(jù),列地址自動(dòng)偏移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]); //寫數(shù)據(jù)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]); //寫數(shù)據(jù)oledTable1}
}/******************************************************************************/
// 函數(shù)名稱:Oled_Show_Char
// 輸入?yún)?shù):oledChar
// 輸出參數(shù):無
// 函數(shù)功能:OLED顯示單個(gè)字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}
5.遠(yuǎn)程控制小車
5.1 藍(lán)牙控制小車
- 使用藍(lán)牙模塊,串口透傳
- 藍(lán)牙模塊,又叫做藍(lán)牙串口模塊
串口透傳技術(shù):
- 透傳即透明傳送,是指在數(shù)據(jù)的傳輸過程中,通過無線的方式這組數(shù)據(jù)不發(fā)生任何形式的改變,仿 佛傳輸過程是透明的一樣,同時(shí)保證傳輸?shù)馁|(zhì)量,原封不動(dòng)地到了最終接收者手里。
- 以太網(wǎng),藍(lán)牙,Zigbee, GPRS 等模塊玩法一樣,對嵌入式程序員來說,不需要關(guān)心通訊模塊內(nèi)部數(shù)據(jù) 及協(xié)議棧工作原理,只要通過串口編程獲得數(shù)據(jù)即可
代碼實(shí)現(xiàn):
//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;//定時(shí)器1工作方式位8位自動(dòng)重裝TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//啟動(dòng)定時(shí)器EA = 1;//開啟總中斷ES = 1;//開啟串口中斷
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//靜態(tài)變量,被初始化一次char tmp;if(RI)//中斷處理函數(shù)中,對于接收中斷的響應(yīng){RI = 0;//清除接收中斷標(biāo)志位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 藍(lán)牙控制并測速小車
原理:運(yùn)用上面講到的藍(lán)牙模塊和測速模塊
代碼實(shí)現(xiàn):
//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; //統(tǒng)計(jì)格子,脈沖次數(shù)
extern unsigned int speed;//速度
extern char signal; //主程序發(fā)速度數(shù)據(jù)的通知
char speedMes[24]; //主程序發(fā)送速度數(shù)據(jù)的字符串緩沖區(qū)void Ex0Init()
{EX0 = 1;//允許中斷//EA = 1;在串口初始化函數(shù)中已經(jīng)打開了總中斷IT0 = 1;//外部中斷的下降沿觸發(fā)
}void main()
{Time0Init();//定時(shí)器0初始化UartInit();//串口相關(guān)初始化//外部中斷初始化Ex0Init();Oled_Init();Oled_Clear();while(1){if(signal){//定時(shí)器1s到點(diǎn),把signal置一,主程序發(fā)送速度sprintf(speedMes,"speed:%d cm/s",speed);//串口數(shù)據(jù)的字符串拼裝,speed是格子,每個(gè)格子1cmSendString(speedMes);//速度發(fā)出去signal = 0;//清0speed,下次由定時(shí)器1s后的中斷處理中再置一}Oled_Show_Str(2,2,speedMes);}
}void speedHandler() interrupt 0 //外部中斷處理函數(shù)
{speedCnt++;//碼盤轉(zhuǎn)動(dòng)了一個(gè)格子
}//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;//定時(shí)器1工作方式位8位自動(dòng)重裝TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//啟動(dòng)定時(shí)器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;//靜態(tài)變量,被初始化一次char tmp;if(RI)//中斷處理函數(shù)中,對于接收中斷的響應(yīng){RI = 0;//清除接收中斷標(biāo)志位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. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD = 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器0中斷ET0 = 1;//5. 打開總中斷EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,經(jīng)過了1ssignal = 1;cnt = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s//計(jì)算小車的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt個(gè)格子,就能算出這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;//就在時(shí)鐘脈沖9期間釋放數(shù)據(jù)線_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做好數(shù)據(jù)準(zhǔn)備sda = dataSend & 0x80;//1000 0000獲得dataSend的最高位,給sda_nop_();//發(fā)送數(shù)據(jù)建立時(shí)間scl = 1;//scl拉高開始發(fā)送_nop_();//數(shù)據(jù)發(fā)送時(shí)間scl = 0;//發(fā)送完畢拉低_nop_();//dataSend = dataSend << 1;}
}void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 寫入指令/數(shù)據(jù)IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 寫入指令/數(shù)據(jù)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//每個(gè)page從0列Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);//0到127列,依次寫入0,每寫入數(shù)據(jù),列地址自動(dòng)偏移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]); //寫數(shù)據(jù)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]); //寫數(shù)據(jù)oledTable1}
}/******************************************************************************/
// 函數(shù)名稱:Oled_Show_Char
// 輸入?yún)?shù):oledChar
// 輸出參數(shù):無
// 函數(shù)功能:OLED顯示單個(gè)字符
/******************************************************************************/
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
- 藍(lán)牙,ESP-01s,Zigbee, NB-Iot等通信模塊都是基于AT指令的設(shè)計(jì)
AT指令介紹:
- AT指令集是從終端設(shè)備(Terminal Equipment,TE)或數(shù)據(jù)終端設(shè)備(Data Terminal Equipment,DTE)向終端適配器(Terminal Adapter,TA)或數(shù)據(jù)電路終端設(shè)備(Data Circuit Terminal Equipment,DCE)發(fā)送的。
- 其對所傳輸?shù)臄?shù)據(jù)包大小有定義:即對于AT指令的發(fā)送,除AT兩個(gè)字符外,最多可以接收1056個(gè) 字符的長度(包括最后的空字符)。
- 每個(gè)AT命令行中只能包含一條AT指令;對于由終端設(shè)備主動(dòng)向PC端報(bào)告的URC指示或者response 響應(yīng),也要求一行最多有一個(gè),不允許上報(bào)的一行中有多條指示或者響應(yīng)。AT指令以回車作為結(jié) 尾,響應(yīng)或上報(bào)以回車換行為結(jié)尾。
代碼實(shí)現(xiàn):
//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; //統(tǒng)計(jì)格子,脈沖次數(shù)
extern unsigned int speed;//速度
extern char signal; //主程序發(fā)速度數(shù)據(jù)的通知
char speedMes[24]; //主程序發(fā)送速度數(shù)據(jù)的字符串緩沖區(qū)
//發(fā)送數(shù)據(jù)
char FSSJ[] = "AT+CIPSEND=0,5\r\n";void Ex0Init()
{EX0 = 1;//允許中斷//EA = 1;在串口初始化函數(shù)中已經(jīng)打開了總中斷IT0 = 1;//外部中斷的下降沿觸發(fā)
}void main()
{Time0Init();//定時(shí)器0初始化UartInit();//串口相關(guān)初始化Delay1000ms();//給espwifi模塊上電時(shí)間initWifi_AP(); //初始化wifi工作在ap模式waitConnect(); //等待客戶端的連接//外部中斷初始化Ex0Init();Oled_Init();Oled_Clear();while(1){if(signal){//定時(shí)器1s到點(diǎn),把signal置一,主程序發(fā)送速度SendString(FSSJ);Delay1000ms();sprintf(speedMes,"%dcms",speed);//串口數(shù)據(jù)的字符串拼裝,speed是格子,每個(gè)格子1cmSendString(speedMes);//速度發(fā)出去signal = 0;//清0speed,下次由定時(shí)器1s后的中斷處理中再置一}Oled_Show_Str(2,2,speedMes);}
}void speedHandler() interrupt 0 //外部中斷處理函數(shù)
{speedCnt++;//碼盤轉(zhuǎn)動(dòng)了一個(gè)格子
}//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返回值的標(biāo)志位
extern char Client_Connect_Flag;void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定時(shí)器1工作方式位8位自動(dòng)重裝TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//啟動(dòng)定時(shí)器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;//靜態(tài)變量,被初始化一次char tmp;if(RI)//中斷處理函數(shù)中,對于接收中斷的響應(yīng){RI = 0;//清除接收中斷標(biāo)志位tmp = SBUF;if(tmp == 'M' || tmp == 'O' || tmp == '0'){i = 0;}buffer[i++] = tmp;//連接服務(wù)器等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. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD = 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器0中斷ET0 = 1;//5. 打開總中斷EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,經(jīng)過了1ssignal = 1;cnt = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的1s//計(jì)算小車的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt個(gè)格子,就能算出這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;//就在時(shí)鐘脈沖9期間釋放數(shù)據(jù)線_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做好數(shù)據(jù)準(zhǔn)備sda = dataSend & 0x80;//1000 0000獲得dataSend的最高位,給sda_nop_();//發(fā)送數(shù)據(jù)建立時(shí)間scl = 1;//scl拉高開始發(fā)送_nop_();//數(shù)據(jù)發(fā)送時(shí)間scl = 0;//發(fā)送完畢拉低_nop_();//dataSend = dataSend << 1;}
}void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 寫入指令/數(shù)據(jù)IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 寫入指令/數(shù)據(jù)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//每個(gè)page從0列Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);//0到127列,依次寫入0,每寫入數(shù)據(jù),列地址自動(dòng)偏移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]); //寫數(shù)據(jù)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]); //寫數(shù)據(jù)oledTable1}
}/******************************************************************************/
// 函數(shù)名稱:Oled_Show_Char
// 輸入?yún)?shù):oledChar
// 輸出參數(shù):無
// 函數(shù)功能:OLED顯示單個(gè)字符
/******************************************************************************/
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返回值的標(biāo)志位
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控制小車
原理:運(yùn)用EC03-DNC4G通信模塊
模塊介紹:
- 基于串口AT指令的開發(fā)方式
- 有兩種工作模式,默認(rèn)是透傳模式,通過其他方式進(jìn)入AT指令模式
- 注意插卡不要出錯(cuò),下圖紅色位置為SIM卡狀態(tài)燈,亮才是正常
代碼不做修改,直接基于藍(lán)牙小車整合,?4g模塊只要做好外網(wǎng)透傳就可以了
6.語音控制小車
6.1語音模塊配置:
使用SU-03T / LD3320
具體介紹看我之前寫過的博客:https://blog.csdn.net/m0_74712453/article/details/13171085
6.2 語音控制小車開發(fā)和調(diào)試代碼
代碼示例:
//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){//前進(jìn)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();//舵機(jī)的初始位置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; //設(shè)置定時(shí)器模式TMOD |= 0x10;TH1 = 0;TL1 = 0;//設(shè)置定時(shí)器0工作模式1,初始值設(shè)定0開始數(shù)數(shù),不著急啟動(dòng)定時(shí)器
}void startHC()
{Trig = 0;Trig = 1;Delay10us();Trig = 0;
}double get_distance()
{double time;//定時(shí)器數(shù)據(jù)清零,以便下一次測距TH1 = 0;TL1 = 0;//1. Trig ,給Trig端口至少10us的高電平startHC();//2. echo由低電平跳轉(zhuǎn)到高電平,表示開始發(fā)送波while(Echo == 0);//波發(fā)出去的那一下,開始啟動(dòng)定時(shí)器TR1 = 1;//3. 由高電平跳轉(zhuǎn)回低電平,表示波回來了while(Echo == 1);//波回來的那一下,我們開始停止定時(shí)器TR1 = 0;//4. 計(jì)算出中間經(jīng)過多少時(shí)間time = (TH1 * 256 + TL1)*1.085;//us為單位//5. 距離 = 速度 (340m/s)* 時(shí)間/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. 配置定時(shí)器0工作模式位16位計(jì)時(shí)TMOD &= 0xF0; //設(shè)置定時(shí)器模式TMOD |= 0x01;//2. 給初值,定一個(gè)0.5出來TL0=0x33;TH0=0xFE;//3. 開始計(jì)時(shí)TR0 = 1;TF0 = 0;//4. 打開定時(shí)器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++; //統(tǒng)計(jì)爆表的次數(shù). cnt=1的時(shí)候,報(bào)表了1//重新給初值TL0=0x33;TH0=0xFE;//控制PWM波if(cnt < jd){sg90_con = 1;}else{sg90_con = 0;}if(cnt == 40){//爆表40次,經(jīng)過了20mscnt = 0; //當(dāng)100次表示1s,重新讓cnt從0開始,計(jì)算下一次的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;//就在時(shí)鐘脈沖9期間釋放數(shù)據(jù)線_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做好數(shù)據(jù)準(zhǔn)備sda = dataSend & 0x80;//1000 0000獲得dataSend的最高位,給sda_nop_();//發(fā)送數(shù)據(jù)建立時(shí)間scl = 1;//scl拉高開始發(fā)送_nop_();//數(shù)據(jù)發(fā)送時(shí)間scl = 0;//發(fā)送完畢拉低_nop_();//dataSend = dataSend << 1;}
}void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 寫入指令/數(shù)據(jù)IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 寫入從機(jī)地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 寫入命令 (0)(1)000000寫入數(shù)據(jù)IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 寫入指令/數(shù)據(jù)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//每個(gè)page從0列Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);//0到127列,依次寫入0,每寫入數(shù)據(jù),列地址自動(dòng)偏移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]); //寫數(shù)據(jù)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]); //寫數(shù)據(jù)oledTable1}
}/******************************************************************************/
// 函數(shù)名稱:Oled_Show_Char
// 輸入?yún)?shù):oledChar
// 輸出參數(shù):無
// 函數(shù)功能:OLED顯示單個(gè)字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}
???????