
??想了解更多關于開源的內容,請訪問:??
??51CTO 開源基礎軟件社區??
??https://ost.51cto.com??
一、前言
最近跑了一遍Bearpi-Micro編寫點亮LED燈程序的Demo,深入了解了如何在開發板上運行一個控制LED燈的程序,達到能關閉燈、開啟燈以及翻轉燈的狀態,南向如何編寫JS API接口提供驅動服務給北向應用使用。突發奇想,發現了官方給出的點燈應用中的一個不足,并進行優化。
參考文章:??編寫通過JS應用控制LED燈??
二、(南向)深入解析通過JS應用控制LED燈
1.前提
請確保已經完成編寫一個點亮LED燈程序實驗,因為本實驗將依賴編寫一個點亮LED燈程序實驗中開發的驅動,以下教程篇幅較長,請耐心仔細閱讀。
2.JS API接口開發
注:以下代碼為主要代碼的剖析,詳細完整的代碼可查看參考文章??編寫通過JS應用控制LED燈??。
(1)添加控制LED燈的JS API接口
修改foundation\ace\ace_engine_lite\frameworks\src\core\modules\app_module.h,加入toggleLed JS API。
static JSIValue ToggleLed(const JSIValue thisVal, const JSIValue* args, uint8_t argsNum);
("##start##“和”##end##"僅用來標識位置,添加完配置后刪除這兩行)。
void InitAppModule(JSIValue exports)
{
JSI::SetModuleAPI(exports, "getInfo", AppModule::GetInfo);
JSI::SetModuleAPI(exports, "terminate", AppModule::Terminate);
##start##
JSI::SetModuleAPI(exports, "ledcontrol", AppModule::ToggleLed);
##end##
#ifdef FEATURE_SCREEN_ON_VISIBLE
JSI::SetModuleAPI(exports, "screenOnVisible", AppModule::ScreenOnVisible);
#endif
}
解析:在頭文件中封裝好JS API接口函數。
提供給北向的接口名為:ledcontrol ,南向業務代碼函數為ToggleLed 。
(2)編寫控制LED燈c++ 業務代碼
在foundation\ace\ace_engine_lite\frameworks\src\core\modules\app_module.cpp中加入控制LED燈c++ 業務代碼
注:以下代碼僅為部分重要代碼。
主函數ToggleLed將會調用的GpioWriteRead函數??
static int GpioWriteRead(struct HdfIoService *serv, int32_t eventData, int32_t *val)
//傳入驅動服務(已在前面的宏定義),命令eventData,待返回的數據指針val
{
int ret = HDF_FAILURE;
struct HdfSBuf *data = HdfSBufObtainDefaultSize(); //獲取256字節的容量
struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); //獲取256字節的容量
if (data == NULL || reply == NULL) {
HILOG_ERROR(HILOG_MODULE_ACE, "fail to obtain sbuf data\n");
return ret;
}
if (!HdfSbufWriteUint8(data, (uint8_t)eventData)) { //將8位無符號整數eventData寫入data
HILOG_ERROR(HILOG_MODULE_ACE, "fail to write sbuf\n");
HdfSBufRecycle(data);
HdfSBufRecycle(reply);
return ret;
}
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data,reply); //調度驅動服務函數,傳入命令LED_WRITE_READ,傳入到驅動的數據指針data,驅動待返回的數據指針reply
if (ret != HDF_SUCCESS) {
HILOG_ERROR(HILOG_MODULE_ACE, "fail to send service call\n");
HdfSBufRecycle(data);
HdfSBufRecycle(reply);
return ret;
}
if (!HdfSbufReadInt32(reply, val)) { //讀取32位整型數reply的值賦給val;如果reply的值為NULL則獲取驅動服務的回復失敗
HILOG_ERROR(HILOG_MODULE_ACE, "fail to get service call reply\n");
ret = HDF_ERR_INVALID_OBJECT;
HdfSBufRecycle(data);
HdfSBufRecycle(reply);
return ret;
}
HILOG_ERROR(HILOG_MODULE_ACE, "Get reply is: %d\n", val); //終端打印消息
HdfSBufRecycle(data); //釋放內存data
HdfSBufRecycle(reply); //釋放內存reply
return ret;
}
解析:已在代碼中注釋。
重要的:Dispatch函數,調度驅動服務呼叫,需要傳入4個參數:*service-驅動服務對象的指針,cmdId-函數的命令字,*data-想要傳入驅動的數據指針,*reply-驅動待回復的數據指針。
接口ToggleLed主函數:
JSIValue AppModule::ToggleLed(const JSIValue thisVal,const JSIValue *args,uint8_t argsNum) //南向接口具體內容,控制LED燈c++ 業務代碼
{
HILOG_ERROR(HILOG_MODULE_ACE, "led button pressed.");
struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE); //綁定HDF驅動中的LED服務
if (serv == NULL) {
HILOG_ERROR(HILOG_MODULE_ACE, "fail to get service2 %s\n", LED_SERVICE);
return JSI::CreateUndefined();
}
if ((args == nullptr) || (argsNum == 0) || (JSI::ValueIsUndefined(args[0]))) {
return JSI::CreateUndefined();
}
JSIValue success = JSI::GetNamedProperty(args[0], CB_SUCCESS); //獲取北向接口中的屬性
JSIValue fail = JSI::GetNamedProperty(args[0], CB_FAIL);
JSIValue complete = JSI::GetNamedProperty(args[0], CB_COMPLETE);
int32_t num = (int32_t)JSI::GetNumberProperty(args[0], "code"); //獲取北向下發的命令code
int32_t replyData = 0;
if (GpioWriteRead(serv, num,&replyData)) { //使用上文定義的GpioWriteRead函數,傳入驅動服務名,命令num,待返回的數據指針
HILOG_ERROR(HILOG_MODULE_ACE, "fail to send event\n");
JSI::CallFunction(fail, thisVal, nullptr, 0);
JSI::CallFunction(complete, thisVal, nullptr, 0);
JSI::ReleaseValueList(success, fail, complete);
return JSI::CreateUndefined();
}
JSIValue result = JSI::CreateObject();
JSI::SetNumberProperty(result, "led_status", replyData); //將replyData的值賦給result
JSIValue argv[ARGC_ONE] = {result};
JSI::CallFunction(success, thisVal, argv, ARGC_ONE);
JSI::CallFunction(complete, thisVal, nullptr, 0);
JSI::ReleaseValueList(success, fail, complete, result);
HdfIoServiceRecycle(serv); //銷毀led的驅動程序服務對象以釋放不再需要的資源。
return JSI::CreateUndefined();
}
解析:獲取北向接口中的屬性success、fail、complete、code。
JSIValue success = JSI::GetNamedProperty(args[0], CB_SUCCESS); //獲取北向接口中的屬性
JSIValue fail = JSI::GetNamedProperty(args[0], CB_FAIL);
JSIValue complete = JSI::GetNamedProperty(args[0], CB_COMPLETE);
int32_t num = (int32_t)JSI::GetNumberProperty(args[0], "code"); //獲取北向下發的命令code

幾個功能相似的賦值函數。
JSI::SetNumberProperty(result, "led_status", replyData); //將replyData的值賦給result
HdfSbufWriteUint8(data, (uint8_t)eventData) //將8位無符號整數eventData寫入data
HdfSbufReadInt32(reply, val)//讀取32位整型數reply的值賦給val
主要流程:北向應用調用ledcontrol接口,南向ToggleLed獲取ledcontrol接口中的屬性,并將屬性值通過驅動服務中的Dispatch函數傳遞進驅動任務中,驅動給出返回值。
三、樣例效果
點擊應用的開關燈,嗯!挺流暢的。但是,如果我只打開北向應用,通過南向命令行終端打開LED燈,北向應用中燈的狀態并不會發生改變。
南北向沒有達到完美的聯動效果。。。

四、優化官方的應用樣例
分析:
從上文的剖析中可以看出南北向的接口似乎沒有優化的地方,北向調用接口,南向就返回燈的狀態值給北向。但是我們通過南向命令行終端執行應用程序的時候雖然也使用到了LED的驅動服務,但是并沒有將燈的狀態值反饋給北向(因為北向沒有調用接口,自然就不會有返回值)。
思路:
既然知道了問題可能出在北向的應用方面,那么就著手嘗試修改/增加一下,讓北向應用中燈的圖片一直調用接口就能一直得到返回的狀態值。既然需要用到接口就得傳參數。經過實驗發現,不管傳入命令code=0(關燈)、1(開燈)、2(轉換燈狀態),效果都極不理想。于是乎深入底層了解LED的驅動服務。以下為主要代碼段
// Dispatch是用來處理用戶態發下來的消息
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{//在這里cmdCode一直都是1,data表示傳入的開關燈命令,reply表示將要返回燈的狀態值
uint8_t contrl;
HDF_LOGE("Led driver dispatch");
if (client == NULL || client->device == NULL)
{
HDF_LOGE("Led driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
switch (cmdCode)
{
/* 接收到用戶態發來的LED_WRITE_READ命令 */
case LED_WRITE_READ:
/* 讀取data里的數據,賦值給contrl */
HdfSbufReadUint8(data,&contrl);
switch (contrl) //在這里判斷傳遞進來的命令
{
/* 開燈 */
case LED_ON: //*data=1,開燈
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
break;
/* 關燈 */
case LED_OFF: //*data=0,關燈
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
break;
/* 狀態翻轉 */
case LED_TOGGLE: //*data=2,轉換燈
if(status == 0)
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
}
else
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
}
break;
default: //*data=其他值時不做操作
break;
}
/* 把LED的狀態值寫入reply, 可被帶至用戶程序 */
if (!HdfSbufWriteInt32(reply, status))
{
HDF_LOGE("replay is fail");
return HDF_FAILURE;
}
break;
default:
break;
}
return HDF_SUCCESS;
}
解決方案
既然如此如果我們在北向傳入其他命令值,比如3,它同樣會調用LED的驅動服務,同樣會返回燈的狀態值,只是沒有進行其他操作。所以在北向可以多寫一個函數為getstatus()??,發送命令3,單純只為獲得燈的狀態值。
getstatus(){
//if(this.statu=='0')this.statu='1'
//else this.statu='0'
try{
let that=this
app.ledcontrol({
code:3,
success(res){
that.statu = res.led_status
},
fail(res,code){
},
complete(){
}
})
}catch{
this.openstatus="調用錯誤"
}
},
效果展示
南向點燈的同時,北向的應用也會實時更新燈的狀態。完美!

??想了解更多關于開源的內容,請訪問:??
??51CTO 開源基礎軟件社區??
??https://ost.51cto.com??。