在之前的一篇推文中,介紹了AB32VG1開發板將模擬量通道7采集到的電壓值實時顯示在OLED顯示屏。雖然之前介紹過AB32VG1采用RT-Thread Studio建立的工程項目基于RT-Thread物聯網操作系統的,但是實時大家看到代碼就會發現,雖然跑了實時操作系統,但是其中的編程方式還是采用的裸機程序編程模式,在main程序while死循環中中調用各種功能函數實現相應功能。具體項目地址:中科藍訊 AB32VG1 開發板ADC采集與顯示實驗。我們知道RTOS編程和裸機編程最大的區別就是RTOS可實現多線程管理,這是RTOS的最大優勢。既然跑了操作系統,為何不用多線程實現ADC采集功能和OLED顯示功能呢?下面我們就重做這個項目,將裸機代碼函數轉換為線程實現這個功能。
1.線程的創建
一個線程要成為可執行的對象就必須由操作系統的內核來為它創建(初始化)一個線程 句柄。可以通過如下的函數接口來創建一個線程。
rt_thread_t rt_thread_create(const char* name, void (*entry)(void* parameter), void* parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick);
調用這個函數時,系統會從動態堆內存中分配一個線程句柄(即TCB,線程控制塊) 以及按照參數中指定的棧大小從動態堆內存中分配相應的空間。分配出來的棧空間是按照 rtconfig.h中配置的RT_ALIGN_SIZE方式對齊。
2.參數介紹:
name是線程的名稱;線程名稱的最大長度由rtconfig.h中定義的 RT_NAME_MAX宏指定,多余部分會被自動截掉。
entry 線程入口函數;
parameter 線程入口函數參數,沒有參數可設置為RT_NULL;
stack_size 線程棧大小,單位是字節。在大多數系統中需要做棧空間地址對 齊(例如ARM體系結構中需要向4字節地址對齊)。
priority 線程的優先級。優先級范圍根據系統配置情況(rtconfig.h中的 RT_THREAD_PRIORITY_MAX宏定義),如果支持的是256級優先 級,那么范圍是從0 ~ 255,數值越小優先級越高,0代表最高優 10 先級。
tick 線程的時間片大小。時間片(tick)的單位是操作系統的時鐘節 拍。當系統中存在相同優先級線程時,這個參數指定線程一次調 度能夠運行的最大時間長度。這個時間片運行結束時,調度器自 動選擇下一個就緒態的同優先級線程進行運行。
3.函數返回
創建成功返回線程句柄;否則返回RT_NULL。
4.案例應用
下面就ADC電壓采集與OLED顯示創建兩個個線程加以說明線程的創建方法:上個項目的main函數完整代碼如下。
#include
#include
#include
#include
#include"board.h"
#include"ssd1306.h"//包含SSD1306的頭文件
#defineADC_DEV_NAME "adc0" /* ADC 設備名稱 */
#defineADC_DEV_CHANNEL 7 /* ADC 通道 */
#defineREFER_VOLTAGE 330 /* 參考電壓 3.3V,數據精度乘以100保留2位小數*/
#defineCONVERT_BITS (1 << 10)?? /* 轉換位數為12位 */
void display(int tmp)
{
//330
unsignedchar count;
unsignedchar datas[] = {0, 0, 0, 0, 0};
datas[0] = tmp / 100;
datas[1] = tmp % 100 / 10;
datas[2] = tmp % 100 % 10;
ssd1306_SetCursor(40, 40);//添加代碼,設置顯示光標位置
ssd1306_WriteChar('0'+datas[0], Font_11x18, White);
ssd1306_WriteChar('.', Font_11x18, White);
for(count = 1; count != 3; count++)
{
ssd1306_WriteChar('0'+datas[count], Font_11x18, White);
}
ssd1306_WriteChar('V', Font_11x18, White);
ssd1306_UpdateScreen();////添加代碼,更新顯示屏信息
}
static int adc_vol_sample()
{
rt_adc_device_t adc_dev;
unsignedchar Temp_Disp_Buff[17];
rt_uint32_t value, vol;
rt_err_t ret = RT_EOK;
/* 查找設備 */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
if (adc_dev == RT_NULL)
{
rt_kprintf("adc sample run failed! can'tfind %s device!\n", ADC_DEV_NAME);
return RT_ERROR;
}
/* 使能設備 */
ret = rt_adc_enable(adc_dev,ADC_DEV_CHANNEL);
/* 讀取采樣值 */
value = rt_adc_read(adc_dev,ADC_DEV_CHANNEL);
rt_kprintf("the value is :%d \n", value);
/* 轉換為對應電壓值 */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* 關閉通道 */
ret = rt_adc_disable(adc_dev,ADC_DEV_CHANNEL);
display(vol);
return ret;
}
int main(void)
{
uint8_t pin = rt_pin_get("PE.1");
staticint advlue;
rt_pin_mode(pin, PIN_MODE_OUTPUT);
rt_kprintf("Hello, world\n");
ssd1306_Init();//添加代碼,顯示屏初始化
ssd1306_SetCursor(2, 6);//添加代碼,設置顯示光標位置
ssd1306_WriteString("The voltage", Font_11x18, White);//添加代碼,設置顯示內容
ssd1306_SetCursor(40, 40);//添加代碼,設置顯示光標位置
display(0);
ssd1306_UpdateScreen();////添加代碼,更新顯示屏信息
while (1)
{
rt_pin_write(pin, PIN_LOW);
rt_thread_mdelay(500);
rt_pin_write(pin, PIN_HIGH);
rt_thread_mdelay(500);
advlue=adc_vol_sample();
}
}
下面我們對上面代碼修改實現多線程,首先將static int adc_vol_sample()和void display(int tmp)改為線程的入口函數,線程的入口函數實際是一個無限循環且不帶返回值的C函數。
首先將void display(int tmp)改為線程入口函數如下形式,注意三點:
第一:將原函數語句放在while(1)循環體內。
第二:while語句循環末尾增加rt_thread_delay(50);延時語句。注意這里不能使用裸機那種的延時,必須用這個延時函數,rt_thread_delay是阻塞延時,調用此函數時,該線程會被掛起,調度器會切換到其他就緒的線程,從而實現多線程。
第三:要定義全局變量rt_uint32_t vol;;vol是要顯示的電壓,其實這里用信號量比較合適,暫時用全局變量吧。
static void display_entry(void* parameter)
{
while(1)
{
unsignedchar count;
unsignedchar datas[] = {0, 0, 0, 0, 0};
datas[0] = vol/ 100;
datas[1] = vol% 100 / 10;
datas[2] = vol% 100 % 10;
ssd1306_SetCursor(40, 40);//添加代碼,設置顯示光標位置
ssd1306_WriteChar('0'+datas[0], Font_11x18, White);
ssd1306_WriteChar('.', Font_11x18, White);
for(count = 1; count != 3; count++)
{
ssd1306_WriteChar('0'+datas[count], Font_11x18, White);
}
ssd1306_WriteChar('V', Font_11x18, White);
ssd1306_UpdateScreen();////添加代碼,更新顯示屏信息
rt_thread_delay(50);
}
}
//創建oled_display線程,一定主要第二個參數入口函數名一定要上面的入口函數名字一致
void oled_display_thread_create()
{
rt_thread_t oled_display_thread;
oled_display_thread = rt_thread_create("oled_display",
display_entry,
RT_NULL,
1024,
RT_THREAD_PRIORITY_MAX / 2,
40);
if (oled_display_thread != RT_NULL)
{
rt_thread_startup(oled_display_thread);
}
}
然后將static int adc_vol_sample()函數改為線程入口函數如下形式,
static void adc_vol_entry(void *parameter)
{
rt_adc_device_t adc_dev;
unsignedchar Temp_Disp_Buff[17];
rt_uint32_t value;
rt_err_t ret = RT_EOK;
/* 查找設備 */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
if (adc_dev == RT_NULL)
{
rt_kprintf("adc sample run failed! can'tfind %s device!\n", ADC_DEV_NAME);
return RT_ERROR;
}
while(1)
{
/* 使能設備 */
ret = rt_adc_enable(adc_dev,ADC_DEV_CHANNEL);
/* 讀取采樣值 */
value = rt_adc_read(adc_dev,ADC_DEV_CHANNEL);
rt_kprintf("the value is :%d \n", value);
/* 轉換為對應電壓值 */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* 關閉通道 */
ret = rt_adc_disable(adc_dev,ADC_DEV_CHANNEL);
rt_thread_delay(50);
}
}
//創建ADC采樣線程
void adc_voltage_thread_create()
{
rt_thread_t adc_voltage_thread;
adc_voltage_thread = rt_thread_create("adc_voltage",
adc_vol_entry,
RT_NULL,
1024,
RT_THREAD_PRIORITY_MAX / 2,
40);
if (adc_voltage_thread != RT_NULL)
{
rt_thread_startup(adc_voltage_thread);
}
}
最后修改main函數如下,將LED燈閃爍代碼刪除,增加線程創建和啟動代碼;
int main(void)
{
//顯示屏初始化
ssd1306_Init();//添加代碼,顯示屏初始化
ssd1306_SetCursor(2, 6);//添加代碼,設置顯示光標位置
ssd1306_WriteString("The voltage", Font_11x18, White);//添加代碼,設置顯示內容
ssd1306_SetCursor(40, 40);//添加代碼,設置顯示光標位
ssd1306_UpdateScreen();////添加代碼,更新顯示屏信息
//線程的創建和啟動
adc_voltage_thread_create();
oled_display_thread_create();
}
至此,代碼修改完畢,編譯下載項目運行,然后在FinSHshell中通過 list_thread()命令查看線程相關信息,如下圖所示,adc_voltage和oled_display兩個線程。更多內容可關注MCU學習筆記。
編輯:fqj
-
OLED
+關注
關注
119文章
6130瀏覽量
223081 -
adc
+關注
關注
97文章
6296瀏覽量
542410 -
多線程
+關注
關注
0文章
275瀏覽量
19850 -
開發板
+關注
關注
25文章
4771瀏覽量
96155 -
RT-Thread
+關注
關注
31文章
1239瀏覽量
39427
發布評論請先 登錄
相關推薦
評論