RT-Thread 當前最新的 nRF24L01 組件這麼用

2021-03-06 MultiMCU EDU

背景介紹:
RT-Thread 的 nRF24L01 組件當前最近的更新發生在 2020-02-04。
Sugar 本篇從使用者的角度說一下最新組件的用法和測試結果。

使用方法

想了解一個 github 上的開源項目,首先就要去看項目的 ReadMe.md

nRF24L01 的開源地址是https://github.com/sogwms/nrf24l01.git,在開源項目上有提到:

點擊 user_guide.md 看到這麼一段代碼:

如果讀者是個玩兒代碼的老手,並且對 RT-Thread 比較熟悉,那麼看到這裡就已經能會用了。當然,為了讓新手也能順利使用,下面 Sugar 還會繼續囉嗦囉嗦。

使用方法看 demo。demo 的截圖這裡就不放了,直接說 Sugar 從 demo 上學到的用法(直接講代碼)。

Sugar 在 RT-Thread 社區上看到這麼個問題:

對於引腳配置,Sugar 沒有使用 env 直接改了代碼(上面圖中說了怎麼改)。如果不知道引腳編號,在代碼裡直接用 rtt 的 GET_PIN() 就行了。

上面說了 CE 和 IRQ 的引腳,片選 CS 的引腳在代碼裡這樣寫的:

rtconfig.h 開啟必要的宏

對於 demo,這樣下來發送角色ROLE_PTX的全部配置就完成了。設計者其實已經做到了極簡配置(只留了 5 個宏定義,都在上面的圖片裡)。

發送失敗怎麼回事

如果讀者在跟著做,到這裡會得到這樣的結果:

上圖來自 RT-Thread 社區,如果讀者遇到了這個現象,那麼恭喜讀者,按照 Sugar 的實測,這樣離成功就不遠了。下面先來解釋一下為什麼發送失敗,其實這個答案開源作者已經說了,但需要再深一步解釋才能讓初學者也看懂,如下:

加入自己的通信數據

關於 RT-Thread 使用 nRF24L01 組件加入自定義通信數據,Sugar 曾經寫過兩篇推文:

1、《為了解決 RT-Thread nRF24L01 軟體包在 STM32F103C8 上死活調不通的問題,我都幹了啥?》

2、《RT-Thread mavlink 甜蜜相擁教程》

新的 nRF24L01 組件包(v2.0.0)與上一版(v1.0.0)不同的是:作者把收和發分別放在了兩個不同的回調函數裡。這與 Sugar 用過的 CubeMX 裸機版驅動的設計一致,關於 CubeMX 裸機驅動 Sugar 寫過一篇推文:

《一起來做遙控車(讓車聽遙控的話)》

nRF24L01 的新組件下把接收放在 rx_ind() 下,把發送放在 tx_done() 下就行了,以 ROLE_PTX 為例,Sugar 給出下面完整代碼:

#include <nrf24l01.h>#include <rtconfig.h>#include <entry.h>#include <rtt_interface.h>#include <mavlink.h>#include <stm32f4xx_hal.h>
#define NRF24_DEMO_ROLE ROLE_PTX#define NRF24_DEMO_SEND_INTERVAL 10#define NRF24_DEMO_SPI_DEV_NAME "spi10"#define NRF24_DEMO_CE_PIN GET_PIN(B, 6)#define NRF24_DEMO_IRQ_PIN GET_PIN(B, 8)
static uint32_t last_timestamp;static uint32_t timestamp;
extern UART_HandleTypeDef huart1;
ap_t mav_data;vel_target vel={.vel_x = 0.0f, .vel_y = 0.0f, .rad_z = 0.0f};uint8_t mode_mav;
static void rx_ind(nrf24_t nrf24, uint8_t *data, uint8_t len, int pipe){ if (len > 0) { uint8_t i; mavlink_message_t msg_receive; mavlink_status_t mav_status; for(i=0; i<32; i++) { if(mavlink_parse_char(0, data[i], &msg_receive, &mav_status)) { last_timestamp = timestamp; switch (msg_receive.msgid) { case MAVLINK_MSG_ID_VELOCITY: { mavlink_velocity_t packet; mavlink_msg_velocity_decode(&msg_receive, &packet); vel.vel_x = packet.vel_x; vel.vel_y = packet.vel_y; vel.rad_z = packet.rad_z;
break; } case MAVLINK_MSG_ID_CMD: { uint8_t myTxData[32]; uint8_t len = mavlink_msg_to_send_buffer( myTxData, &msg_receive ); HAL_UART_Transmit(&huart1,myTxData,len,10); break; } case MAVLINK_MSG_ID_MODE: { mavlink_mode_t packet; mavlink_msg_mode_decode(&msg_receive, &packet); uint8_t mode_msg[2]; mode_msg[0] = packet.mode; mode_msg[1] = packet.reason; rt_err_t uwRet = RT_EOK; uwRet = rt_mq_send(mode_mq, mode_msg, sizeof(mode_msg)); if(RT_EOK != uwRet) { rt_kprintf("data can not send to message queue, code: %lx\n", uwRet); } rt_sem_release(mode_sem); break; } } } } }}
static void tx_done(nrf24_t nrf24, int pipe){ mavlink_message_t msg; char tbuf[32]; mav_data.mode = mode_mav; timestamp = HAL_GetTick(); if(abs(timestamp - last_timestamp) > 1000){ memset(&vel, 0, sizeof(vel_target)); mav_data.com = 1; } else { mav_data.com = 0; } mavlink_msg_simple_pack(0,0,&msg,mav_data.value); mavlink_msg_to_send_buffer((uint8_t *)tbuf, &msg); nrf24_send_data(nrf24, (uint8_t *)tbuf, sizeof(tbuf), pipe); rt_thread_mdelay(NRF24_DEMO_SEND_INTERVAL);}
const static struct nrf24_callback _cb = { .rx_ind = rx_ind, .tx_done = tx_done,};
static void thread_entry(void *param){ nrf24_t nrf24;
rt_kprintf("[nrf24/demo] Version:%s\n", PKG_NRF24L01_VERSION);
nrf24 = nrf24_default_create(NRF24_DEMO_SPI_DEV_NAME, NRF24_DEMO_CE_PIN, NRF24_DEMO_IRQ_PIN, &_cb, NRF24_DEMO_ROLE);
if (nrf24 == RT_NULL) { rt_kprintf("\n[nrf24/demo] Failed to create nrf24. stop!\n"); for(;;) rt_thread_mdelay(10000); } else { rt_kprintf("[nrf24/demo] running."); }
nrf24_send_data(nrf24, "Hi\n", 3, NRF24_DEFAULT_PIPE);
while (1) { nrf24_run(nrf24);
if(!nrf24->flags.using_irq) rt_thread_mdelay(10); } }
static int nrf24l01_sample_init(void){ rt_thread_t thread;
thread = rt_thread_create("nrf_mav", thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX/2, 20); rt_thread_startup(thread);
return RT_EOK;}
INIT_APP_EXPORT(nrf24l01_sample_init);

與之相應的 PRX 的代碼請看 Sugar 開源遙控器項目:

https://github.com/SuWeipeng/xm_rc

啊,爆露了!上圖顯示 Sugar 恢復了上一版代碼,為什麼會這樣呢?

測試過程

Sugar 最近不論是車控還是遙控項目,都在做一件事:一個項目開始支持多種硬體平臺。做這件事的時候用新版的 nRF24L01 組件代碼,發現兩個問題:

1、正點的遙控發了 ACK,但車控無論如何都收不到回復內容;
2、車控同型號的板子,同型號的直插式 nRF24L01 模塊,有些組合無法工作。能工作的組合也要看運氣,並不是每次上電都正常工作。

上面這些問題讓 Sugar 決定用回上個版本。上個版本 Sugar 測過 F103C8、F407VE、L475VE與幾種直插式 nRF24L01 組合都沒有問題。

在公司裡工作的技術人員會有一種體會:當出現問題的時候做軟體的和做硬體的都有理由說自己這裡沒問題,然而產品工作就是不正常。解決這類問題第一靠「人品」,第二靠「耐心」。

第一靠「人品」:以前 Sugar 工作中遇到這類問題會把硬體、軟體的人組織到一起找問題,一堆人在一起面對問題的時候人品立現。有些人會第一時間靜下心來看已有的測試記錄,有些人不看測試記錄上來就先把鍋甩出去。當團隊裡第一種人多的時候問題解決效率是明顯高的,所以說第一靠「人品」。

第二靠「耐心」:能坐到一起解決技術問題是緣份,不論技術水平高低既然能坐上來就一定有自己的閃光點。找問題就是匯聚所有的閃光點,從各個角度照亮每一個細節的過程,這是需要極大耐心的。找問題最常用的辦法是「交叉測試」。

新組件的交叉測試:
1、Sugar 先用上一版穩定代碼確認出了 3 塊車控(兩個 F407VE、一個 F103C8) 4 個 nRF24L01 硬體完好可用;
2、好的硬體換上新版的代碼,用 F103C8 車控和小馬哥手柄測試 100% 好用;
3、換用 F407 車控和小馬哥手柄,有時好用、有時不好用;
4、不論哪種車控,用正點原子的手柄都不行。

當車控與直插式 nRF24L01 出問題的時候,DEBUG 最常卡在下面這句:

當用正點原子手柄的時候,在手柄端 DEBUG 發現數據確實通過nrf24_send_data()接口發送了,但車控就是收不到。

因為 F407 車控與小馬哥手柄有時好用, F103 車控和小馬哥手柄 100% 好用,所以 Sugar 本文說的使用方法就是對的。已經通過上一版組件確認所有硬體是好的,所以定位在:新版 nRF24L01 組件還有改善空間。

關注作者

歡迎掃碼關注我的公眾號MultiMCU EDU。

提示:在公眾號「關於我」頁面可加作者微信好友。喜歡本文求點讚,有打賞我會更有動力。

相關焦點

  • Debug神器 | RT-Thread發布「超輕量級「日誌組件ulog
    RT-Thread 一直缺少小巧、實用的日誌組件,而 ulog 的誕生補全了這塊的短板。今天,它將作為 RT-Thread 的基礎組件被開源出來,讓我們的開發者也能用上簡潔易用的日誌系統,提高開發效率。
  • Linux系統下ESP32開發板搭建RT-Thread開發環境
    獲取代碼cd ~git clonehttps://github.com/BernardXiong/rtthread-esp-idf cd rtthread-esp-idfgit submodule initgit submodule update cd esp-idf-port/
  • RT-Thread Smart上手指南~
    編譯成功後,`userapps/apps`下的應用程式會編譯成一個個的elf可執行文件,並放置於`userapps/root/bin`目錄下,可以把它轉換成romfs、C語言數組的方式給rt-smart內核使用,這樣可以不依賴於其他文件系統就可以直接執行,生成romfs、C語言數組可以用如下的命令行:1cd userapps2python ..
  • 文件系統靈活用——智能家居DIY連載教程3
    不過,能巧妙靈活的把文件系統用好用對,可不是一件輕鬆的事情。DFS 是 RT-Thread 提供的虛擬文件系統組件,全稱為 Device File System,即設備虛擬文件系統,文件系統的名稱使用類似 UNIX 文件、文件夾的風格。
  • RT-Thread Nano實戰2-msh配置與運行
    uart主要用到了RT-Thread的device驅動組件和ringbuffer組件。和RT-Thread標準版bsp下的serial驅動有一些區別,具體可參照第5部分.相關閱讀RT-Thread常使用rt_hw_usart_init()函數,也可自定義函數名。
  • RT-Thread Smart 上手指南
    編譯成功後,`userapps/apps`下的應用程式會編譯成一個個的elf可執行文件,並放置於`userapps/root/bin`目錄下,可以把它轉換成romfs、C語言數組的方式給rt-smart內核使用,這樣可以不依賴於其他文件系統就可以直接執行,生成romfs、C語言數組可以用如下的命令行:1cd userapps2python ..
  • 基於rt-thread功耗調優與PM管理實戰經驗分享-調試篇
    背景上篇講了一點功耗管理的概念,理解了功耗管理與優化原來有這麼多的東西在裡面,分分鐘搞定的事情,越來越難做了!
  • RT-Thread教程一之Linux下開發環境及QEMU配置
    RT-Thread原始碼Linux下安裝git後在工程目錄下(比~/Workplace)執行git clone https://github.com/RT-Thread/rt-thread 我們看一下樹目錄結構➜  rt-thread git:(master) ✗ tree . -L 1.
  • RT-Thread RTC設備學習筆記
    2、RTC設備操作接口RT-Thread為RTC設備提供了三個用戶層次的應用操作接口,分別是設置日期、設置時間和獲取當前時間。在RT-Thread的設備中,有且僅有一個RTC設備,設備名稱為"rtc"。
  • 當「樹莓派」遇上RT-Thread Smart——應用編程入門
    樹莓派4B包括了4核的ARM Cortex-A72,1.5GHz的BCM2711晶片,可以執行ARM AArch64位指令,也可以執行ARM AArch32位指令,具備標準化的通用控制器GIC。和樹莓派3B+的硬體規格對比情況:   要在樹莓派上運行smart也很簡單,直接下載smart的發布版,裡面有樹莓派4B上對應的移植代碼,及一些用戶態應用程式。
  • RT-Thread 編程風格指南
    1.目錄名稱目錄名稱如果無特殊的需求,請使用全小寫的形式;目錄名稱應能夠反應部分的意思,例 如各晶片移植由其晶片名稱構成或晶片類別構成;components 目錄下能夠反映組件的意義。2.文件名稱文件名稱如果無特殊的需求(如果是引用其他地方,可以保留相應的名稱),請使用全小寫 的形式。另外為了避免文件名重名的問題,一些地方請儘量不要使用通用化、使用頻率高 的名稱。
  • RT-Thread ADC設備學習筆記
    2.1 硬體原理圖參考這篇文章:基於小熊派氣體傳感器MQ-2綜合實踐2.2 軟體功能實現根據官方給出的文檔可以分為這麼幾步操作:查找設備 rt_device_t rt_device_find(const char* name);參數描述nameADC 設備名稱返回--設備句柄查找到對應設備將返回相應的設備句柄RT_NULL沒有找到設備使能設備rt_err_t rt_adc_enable
  • RT-Thread+STM32實現智能車目標識別系統的教程
    下面是 main.cpp 的內容,其實就是初始化了電機,然後發布了 2 個話題 (topic),一個是 /vel_x 告訴 ROS 當前小車的速度,一個是 /turn_bias 告訴 ROS 當前小車的旋轉速度。
  • RT-Thread Nano如何適配pin設備API,並在RT-Thread Nano使用軟體包
    BearPI-IOT board為什麼需要設備接口RT-Thread 分為標準版本和 Nano 版本,其特點如下:RT-Thread 標準版:擁有設備驅動框架,軟體包等組件,軟體包都是基於設備驅動接口來實現。RT-Thread Nano:僅僅只是一個 RTOS 內核。沒有任何組件。Nano 是無法直接使用 RT-Thread 豐富軟體包功能。
  • RT-Thread Nano如何適配Pin設備API,並在RT-Thread Nano使用軟體包
    BearPI-IOT board為什麼需要設備接口RT-Thread 分為標準版本和 Nano 版本,其特點如下:RT-Thread 標準版:擁有設備驅動框架,軟體包等組件,軟體包都是基於設備驅動接口來實現。RT-Thread Nano:僅僅只是一個 RTOS 內核。沒有任何組件。Nano 是無法直接使用 RT-Thread 豐富軟體包功能。
  • RT-Thread Studio開發環境搭建
    從今天開始,我們學習使用RT-Thread Studio IDE開發STM32程序,RT-Thread Studio是一站式的 RT-Thread 開發工具,通過簡單易用的圖形化配置系統以及豐富的軟體包和組件資源
  • RT-Thread 入門學習筆記 - 查看線程棧的地址
    _t stack_size; 仿照list_thread編寫測試代碼void dump_thread_ex(void){ rt_uint8_t index = 0; rt_uint8_t obj_size = 0; int thread_cnt = rt_object_get_length
  • 如何向RT-Thread提交一個BSP?
    rt-thread所遵循的開源協議在貢獻代碼之前,我們有必要先來了解一下開源項目所遵循的協議,如果你提交成功,開源協議將會約束這些代碼被如何使用。Fork將遠程倉庫下載到本地:git clone https://github.com/username/rt-thread,這樣就會把遠程代碼下載到本地。
  • RT-Thread時鐘管理學習總結
    關於RT-Thread時鐘管理相關的內容,官方提供了比較豐富的文檔作為參考,具體可以查看以下連結:https://www.rt-thread.org/document/site/programming-manual/timer/timer/本文嘗試從以下幾個方面總結一下RT-Thread時鐘管理的學習過程。
  • RT-Thread智能車目標識別系統連載教程——連接 ROS 小車控制(5)
    ROS 當前小車的旋轉速度。 thread = rt_thread_create("rosserial",     rosserial_thread_entry, RT_NULL, 2048, 8, 10);78    if(thread !