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