OpenHarmony設(shè)備開發(fā)(五)-超聲波傳感器
??想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:??
前言
在之前學(xué)習(xí)了很多新的有趣知識(shí),本次學(xué)習(xí)了超聲波模組,利用已學(xué)的知識(shí)給遙控小車加上一個(gè)簡(jiǎn)單的自動(dòng)避障功能。
有了超聲波模組,以后可以開發(fā)更有意思的小實(shí)驗(yàn),例如簡(jiǎn)單的無(wú)人操控小車、簡(jiǎn)單的量尺…
準(zhǔn)備
創(chuàng)建文件夾和相關(guān)的代碼文件。
其中WIFI_car保存網(wǎng)絡(luò)連接遙控小車的代碼,WIFI_hcsr04保存超聲波模組相關(guān)的代碼。
編輯BUILD.gn,加入相關(guān)頭文件的引用路徑(小tips:不知道路徑的頭文件可以用Ctrl+P搜索)。
static_library("WIFI_car_demo") {
sources = [
"WIFI_car.c",
"WIFI_hcsr04.c",
]
include_dirs = [
"http://utils/native/lite/include",
"http://kernel/liteos_m/components/cmsis/2.0",
"http://base/iot_hardware/peripheral/interfaces/kits",
"http://ohos_bundles/@ohos/device_soc_hisilicon/hi3861v100/sdk_liteos/include",
"http://ohos_bundles/@ohos/device_soc_hisilicon/hi3861v100/sdk_liteos/third_party/lwip_sack/include",
]
}
編輯app路徑下的BUILD.gn,使我們的遙控小車能被系統(tǒng)編譯加載。
格式是:"文件夾名:靜態(tài)庫(kù)名"。
超聲波模組
本次樣例學(xué)習(xí)的是HC-SR04超聲波模組。
超聲波模組一共有4個(gè)引腳,分別為Vcc、 Trig(控制端)、 Echo(接收端)、 GND;其中VCC、GND接上5V電源。
HC-SR04超聲波距離傳感器的核心是兩個(gè)超聲波傳感器。一個(gè)Trig(控制端)控制發(fā)出的超聲波信號(hào),將電信號(hào)轉(zhuǎn)換為40 KHz超聲波脈沖。Echo(接收端)監(jiān)聽發(fā)射的脈沖。
初始化
通過查詢?cè)韴D,可以得知小車連接超聲波的GPIO口為7和8。
其中GPIO7是Trig(輸入口),GPIO8是Echo(輸出口)。
首先我們要對(duì)這兩個(gè)GPIO口進(jìn)行初始化。
GPIO口初始化老三套:IO口初始化、IO口復(fù)用、IO口輸入輸出方向。
int hcsr_gpio_init(void)
{
IoTGpioInit(GPIO_Trig);
hi_io_set_func(GPIO_Trig, 0); //0是普通IO口的意思
IoTGpioSetDir(GPIO_Trig, IOT_GPIO_DIR_OUT);
IoTGpioInit(GPIO_Echo);
hi_io_set_func(GPIO_Echo, 0);
IoTGpioSetDir(GPIO_Echo, IOT_GPIO_DIR_IN);
}
編寫檢測(cè)距離函數(shù)
我們要檢測(cè)距離,首先要懂得超聲波傳感器的原理。
超聲波傳感器通過Trig高電平發(fā)送聲波,Echo接收超聲波,通過計(jì)算發(fā)送和接收到的時(shí)間差,輔以聲波的速度,得以計(jì)算出小車和障礙之間的距離。
程序設(shè)計(jì)流程:
- 發(fā)送trig高電平。
- 等待20us,trig設(shè)置為低電平。
- Echo接收到了高電平,計(jì)時(shí)高電平時(shí)間,高電平持續(xù)時(shí)間就是超聲波從發(fā)射到返回的總時(shí)間。
其中為了減少干擾,可以先發(fā)送trig高電平50us,再將trig置為低電平,Echo接收到的數(shù)據(jù)從高電平降為低電平,便可開始。
為了準(zhǔn)確性,還可以檢測(cè)數(shù)次距離,再對(duì)其數(shù)據(jù)做平均值以及除錯(cuò)(當(dāng)聲波沒有被反射回來,則回波信號(hào)將在38毫秒后超時(shí)并返回低電平。因此38 ms的脈沖表示在傳感器范圍內(nèi)沒有阻塞。)
本次樣例只簡(jiǎn)單編寫了一個(gè)檢測(cè)函數(shù):
float GetDistance(void)
{
IotGpioValue value = IOT_GPIO_VALUE0;
float distance = 0.0;
int flag = 0;
static unsigned long start_time = 0, time = 0;
//發(fā)送聲波
IoTGpioSetOutputVal(GPIO_Trig, IOT_GPIO_VALUE1);
hi_udelay(20);
IoTGpioSetOutputVal(GPIO_Trig, IOT_GPIO_VALUE0);
while (1)
{//不斷檢測(cè)聲波
IoTGpioGetInputVal(GPIO_Echo, &value);
//記錄下高電平持續(xù)時(shí)間
if (value == IOT_GPIO_VALUE1 && flag == 0)
{
start_time = hi_get_us();
flag = 1;
}
if (value == IOT_GPIO_VALUE0 && flag == 1)
{
time = hi_get_us() - start_time;
start_time = 0;
break;
}
}
distance = time * 0.034 / 2;
printf("distance is %f\r\n", distance);
return distance;
}
避障線程
簡(jiǎn)單的避障功能需要以下幾點(diǎn)需求:
- 避障線程的優(yōu)先級(jí)需要高于遙控的優(yōu)先級(jí),這樣當(dāng)快要撞上障礙時(shí)能保證自動(dòng)避障能正常運(yùn)行。
- 需要設(shè)計(jì)一個(gè)信號(hào)量,信號(hào)量的作用是保證兩個(gè)或多個(gè)關(guān)鍵代碼不被并發(fā)調(diào)用,在這里是為了避免避障線程和遙控線程并發(fā)調(diào)用。本次樣例使用一個(gè)sem_d變量代替了信號(hào)量,sem_d為1時(shí)使遙控功能睡眠。
- 不斷判斷距離是否小于特定值,當(dāng)小于特定值做出相對(duì)應(yīng)的避障。
void hcsr04_avoid(void)
{
float distance = 0;
//io口初始化
hcsr_gpio_init();
while (1)
{
//獲取距離信息
distance=GetDistance();
if (distance < 20)
{
printf("Distance <20!!!");
sem_d = 1;
car_stop();
car_backward();
sleep(1);
car_stop();
sem_d = 0;
}
}
}
void hcsr04_demo(void)
{
osThreadAttr_t attr;
attr.name = "hcsr04_Task";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 4096;
attr.priority = 24;
if (osThreadNew((osThreadFunc_t)hcsr04_avoid, NULL, &attr) == NULL)
{
printf("[hcsr04_Task] Falied to create hcsr04_Task!\n");
}
}
網(wǎng)絡(luò)連接遙控小車
源代碼文件
為了加入超聲波傳感器線程,我們可以在小車IO口之后啟動(dòng)超聲波線程(也可直接用SYS_RUN()啟動(dòng)線程,此處因?yàn)檫b控之前用不到避障,因而延遲啟動(dòng)線程):
extern void hcsr04_demo(void);
hcsr04_demo();
信號(hào)量的使用:
首先創(chuàng)建全局變量,記得要在頭文件中extern該變量,WIFI_hcsr04.c才能使用到該變量。
當(dāng)sem_d為1時(shí),休眠遙控代碼,直至sem_d為0。
int sem_d=0;
while(1)
{
if(sem_d==1)
{
continue;
}
//遙控小車代碼...
}
頭代碼文件
此處代碼為了WIFI_hcsr04.c能調(diào)用到一些需要用到的函數(shù)和變量。
void car_stop(void);
void car_backwward(void);
extern int sem_d;
其它
效果
問題
測(cè)試了幾輪下來,發(fā)現(xiàn)了有以下問題。
- 在測(cè)試中偶爾會(huì)遇到棧溢出的情況,原因尚未找到。
- 超聲波傳感器的精確度不高,容易會(huì)誤判障礙物(虛無(wú)的障礙emm)。
當(dāng)前解決方案:
- 加大線程的棧大小(無(wú)法從根本解決)。
- 找到棧溢出原因,設(shè)計(jì)超時(shí)機(jī)制,避免內(nèi)存不斷堆積。
- 設(shè)計(jì)檢測(cè)距離的算法,對(duì)獲取的數(shù)據(jù)進(jìn)行取平均值以及除去最大最小。
float distance_sum[5];
float distance = 0;
//獲取數(shù)據(jù)
for (int i = 0; i < 5; i++)
{
distance_sum[i] = GetDistance();
}
int max_id = 0;
int min_id = 0;
//記錄最大最小的數(shù)組下標(biāo)
for (int i = 1; i < 5; i++)
{
if (distance_sum[i] > distance_sum[max_id])
{
max_id = i;
}
if (distance_sum[i] < distance_sum[min_id])
{
min_id = i;
}
}
for (int i = 0; i < 5; i++)
{
if (i != max_id && i != min_id)
{
distance += distance_sum[i];
}
}
distance /= 3.0;