在之前的一篇推文中,介紹了AB32VG1開發(fā)板將模擬量通道7采集到的電壓值實時顯示在OLED顯示屏。雖然之前介紹過AB32VG1采用RT-Thread Studio建立的工程項目基于RT-Thread物聯(lián)網(wǎng)操作系統(tǒng)的,但是實時大家看到代碼就會發(fā)現(xiàn),雖然跑了實時操作系統(tǒng),但是其中的編程方式還是采用的裸機程序編程模式,在main程序while死循環(huán)中中調(diào)用各種功能函數(shù)實現(xiàn)相應功能。具體項目地址:中科藍訊 AB32VG1 開發(fā)板ADC采集與顯示實驗。我們知道RTOS編程和裸機編程最大的區(qū)別就是RTOS可實現(xiàn)多線程管理,這是RTOS的最大優(yōu)勢。既然跑了操作系統(tǒng),為何不用多線程實現(xiàn)ADC采集功能和OLED顯示功能呢?下面我們就重做這個項目,將裸機代碼函數(shù)轉(zhuǎn)換為線程實現(xiàn)這個功能。
1.線程的創(chuàng)建
一個線程要成為可執(zhí)行的對象就必須由操作系統(tǒng)的內(nèi)核來為它創(chuàng)建(初始化)一個線程 句柄。可以通過如下的函數(shù)接口來創(chuàng)建一個線程。
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);
調(diào)用這個函數(shù)時,系統(tǒng)會從動態(tài)堆內(nèi)存中分配一個線程句柄(即TCB,線程控制塊) 以及按照參數(shù)中指定的棧大小從動態(tài)堆內(nèi)存中分配相應的空間。分配出來的棧空間是按照 rtconfig.h中配置的RT_ALIGN_SIZE方式對齊。
2.參數(shù)介紹:
name是線程的名稱;線程名稱的最大長度由rtconfig.h中定義的 RT_NAME_MAX宏指定,多余部分會被自動截掉。
entry 線程入口函數(shù);
parameter 線程入口函數(shù)參數(shù),沒有參數(shù)可設置為RT_NULL;
stack_size 線程棧大小,單位是字節(jié)。在大多數(shù)系統(tǒng)中需要做棧空間地址對 齊(例如ARM體系結(jié)構(gòu)中需要向4字節(jié)地址對齊)。
priority 線程的優(yōu)先級。優(yōu)先級范圍根據(jù)系統(tǒng)配置情況(rtconfig.h中的 RT_THREAD_PRIORITY_MAX宏定義),如果支持的是256級優(yōu)先 級,那么范圍是從0 ~ 255,數(shù)值越小優(yōu)先級越高,0代表最高優(yōu) 10 先級。
tick 線程的時間片大小。時間片(tick)的單位是操作系統(tǒng)的時鐘節(jié) 拍。當系統(tǒng)中存在相同優(yōu)先級線程時,這個參數(shù)指定線程一次調(diào) 度能夠運行的最大時間長度。這個時間片運行結(jié)束時,調(diào)度器自 動選擇下一個就緒態(tài)的同優(yōu)先級線程進行運行。
3.函數(shù)返回
創(chuàng)建成功返回線程句柄;否則返回RT_NULL。
4.案例應用
下面就ADC電壓采集與OLED顯示創(chuàng)建兩個個線程加以說明線程的創(chuàng)建方法:上個項目的main函數(shù)完整代碼如下。
#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,數(shù)據(jù)精度乘以100保留2位小數(shù)*/
#defineCONVERT_BITS (1 << 10)?? /* 轉(zhuǎn)換位數(shù)為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);
/* 轉(zhuǎn)換為對應電壓值 */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* 關(guān)閉通道 */
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);//添加代碼,設置顯示內(nèi)容
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();
}
}
下面我們對上面代碼修改實現(xiàn)多線程,首先將static int adc_vol_sample()和void display(int tmp)改為線程的入口函數(shù),線程的入口函數(shù)實際是一個無限循環(huán)且不帶返回值的C函數(shù)。
首先將void display(int tmp)改為線程入口函數(shù)如下形式,注意三點:
第一:將原函數(shù)語句放在while(1)循環(huán)體內(nèi)。
第二:while語句循環(huán)末尾增加rt_thread_delay(50);延時語句。注意這里不能使用裸機那種的延時,必須用這個延時函數(shù),rt_thread_delay是阻塞延時,調(diào)用此函數(shù)時,該線程會被掛起,調(diào)度器會切換到其他就緒的線程,從而實現(xiàn)多線程。
第三:要定義全局變量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);
}
}
//創(chuàng)建oled_display線程,一定主要第二個參數(shù)入口函數(shù)名一定要上面的入口函數(shù)名字一致
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()函數(shù)改為線程入口函數(shù)如下形式,
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);
/* 轉(zhuǎn)換為對應電壓值 */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* 關(guān)閉通道 */
ret = rt_adc_disable(adc_dev,ADC_DEV_CHANNEL);
rt_thread_delay(50);
}
}
//創(chuàng)建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函數(shù)如下,將LED燈閃爍代碼刪除,增加線程創(chuàng)建和啟動代碼;
int main(void)
{
//顯示屏初始化
ssd1306_Init();//添加代碼,顯示屏初始化
ssd1306_SetCursor(2, 6);//添加代碼,設置顯示光標位置
ssd1306_WriteString("The voltage", Font_11x18, White);//添加代碼,設置顯示內(nèi)容
ssd1306_SetCursor(40, 40);//添加代碼,設置顯示光標位
ssd1306_UpdateScreen();////添加代碼,更新顯示屏信息
//線程的創(chuàng)建和啟動
adc_voltage_thread_create();
oled_display_thread_create();
}
至此,代碼修改完畢,編譯下載項目運行,然后在FinSHshell中通過 list_thread()命令查看線程相關(guān)信息,如下圖所示,adc_voltage和oled_display兩個線程。更多內(nèi)容可關(guān)注MCU學習筆記。
編輯:fqj
-
OLED
+關(guān)注
關(guān)注
119文章
6198瀏覽量
224122 -
adc
+關(guān)注
關(guān)注
98文章
6496瀏覽量
544482 -
多線程
+關(guān)注
關(guān)注
0文章
278瀏覽量
19946 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
5035瀏覽量
97383 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1285瀏覽量
40093
發(fā)布評論請先 登錄
相關(guān)推薦
評論