资讯详情

AB32VG1 开发板ADC采集与显示的多线程实现

在之前的推文中,介绍了AB32VG1开发板实时显示模拟量通道7采集的电压值OLED显示屏。虽然之前介绍过AB32VG1采用RT-Thread Studio基于建立的项目RT-Thread物联网操作系统,但当你实时看到代码时,你会发现,虽然实时操作系统已经运行,但编程模式仍然采用裸机程序编程模式main程序while在死循环中调用各种功能函数来实现相应的功能。具体项目地址:中科蓝讯 AB32VG1 开发板ADC采集和显示实验。我们知道RTOS编程和裸机编程最大的区别是RTOS多线程管理是可以实现的RTOS最大优势。既然跑了操作系统,为什么不用多线程实现呢?ADC采集功能和OLED显示功能呢?让我们重做这个项目,将裸机代码函数转换为线程来实现这个功能。

要成为可执行的对象,必须由操作系统的核心创建(初始化)线程 句柄。线程可以通过以下函数接口创建。

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方式对齐。

name是线程名称;线程名称的最大长度由rtconfig.h中定义的 RT_NAME_MAX宏指定,多余部分将自动切断。

entry 线程入口函数;

parameter 无参数可设置为线程入口函数参数RT_NULL;

stack_size 线程栈的大小,单位是字节。在大多数系统中,需要做栈空间地址 齐(例如ARM系统结构需要对齐4字节地址)。

priority 线程优先级。优先级范围根据系统配置(rtconfig.h中的 RT_THREAD_PRIORITY_MAX宏定义),如果支持的是256级优先 等级,范围为0 ~ 数值越小,优先级越高,0代表最高优先级 10 先级。

tick 线程时间片大小。时间片(tick)操作系统的时钟是单位 拍。当系统中有相同的优先线程时,参数指定线程一次调整 最大长度可以运行。当时间片运行结束时,调度器从 动态选择下一个线程来运行同样优先级的线程。

创建成功的返回线程句柄;否则返回RT_NULL。

下面就ADC电压采集与OLED显示创建两个线程来解释线程的创建方法:上一个项目main函数完整代码如下。

<rtthread.h>

<rtdevice.h>

<string.h>

<stdio.h>

"board.h"

"ssd1306.h"//包含SSD1306的头文件

ADC_DEV_NAME "adc0"/* ADC 设备名称 */

ADC_DEV_CHANNEL 7 /* ADC 通道 */

REFER_VOLTAGE 330 /* 参考电压 3.3V,数据精度乘以100*/

CONVERT_BITS (1 << 10) /* 转换位数为12位 */

( tmp)

{

//330

count;

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);

(count = 1; count != 3; count )

{

ssd1306_WriteChar('0' datas[count], Font_11x18, White);

}

ssd1306_WriteChar('V', Font_11x18, White);

ssd1306_UpdateScreen();添加代码,更新显示信息

}

()

{

rt_adc_device_t adc_dev;

Temp_Disp_Buff[17];

rt_uint32_t value, vol;

rt_err_t ret = RT_EOK;

/* 查找设备 */

adc_dev = (rt_adcdevice_t)rt_device_find(ADC_DEV_NAME);

    (adc_dev == RT_NULL)

    {

        rt_kprintf("adc sample run failed! can'tfind %s device!\n", ADC_DEV_NAME);

        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);

    ret;

}

()

{

    uint8_t pin = rt_pin_get("PE.1");

    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();添加代码,更新显示屏信息

    (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();

    }

}

      下面我们对上面代码修改实现多线程,首先将 ()和 ( tmp)改为线程的入口函数,线程的入口函数实际是一个无限循环且不带返回值的C函数。

      首先将 ( tmp)改为线程入口函数如下形式,注意三点:

第一:将原函数语句放在while(1)循环体内。

第二:while语句循环末尾增加rt_thread_delay(50);延时语句。注意这里不能使用裸机那种的延时,必须用这个延时函数,rt_thread_delay是阻塞延时,调用此函数时,该线程会被挂起,调度器会切换到其他就绪的线程,从而实现多线程。

第三:要定义全局变量rt_uint32_t  vol;;vol是要显示的电压,其实这里用信号量比较合适,暂时用全局变量吧。

_entry( parameter)

{

    while(1)

   {

    count;

    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);

    (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);     } }

()函数改为线程入口函数如下形式,

void adc_vol_entry(void *parameter)

{

    rt_adc_device_t adc_dev;

    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);

    (adc_dev == RT_NULL)

    {

        rt_kprintf("adc sample run failed! can'tfind %s device!\n", ADC_DEV_NAME);

        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灯闪烁代码删除,增加线程创建和启动代码;

()

{

    //显示屏初始化

    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两个线程。

图片

 

喜欢此内容的人还喜欢

标签: 传感器tcbab电压传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台