0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

通过多线程来实现ADC采集功能和OLED显示功能

MCU学习笔记 来源:MCU学习笔记 作者:MCU学习笔记 2021-11-20 09:30 次阅读

在之前的一篇推文中,介绍了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_voltageoled_display两个线程。更多内容可关注MCU学习笔记。

poYBAGGYTwyAcCuAACUIG9WRAJM112.pngpoYBAGGYTvSAM_sKAAGbq0rdGVE117.png

编辑:fqj

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • OLED
    +关注

    关注

    119

    文章

    6167

    浏览量

    223617
  • adc
    adc
    +关注

    关注

    97

    文章

    6381

    浏览量

    543654
  • 多线程
    +关注

    关注

    0

    文章

    277

    浏览量

    19891
  • 开发板
    +关注

    关注

    25

    文章

    4880

    浏览量

    96998
  • RT-Thread
    +关注

    关注

    31

    文章

    1257

    浏览量

    39809
收藏 人收藏

    评论

    相关推荐

    基于TCP/IP协议和多线程的通信软件的设计与实现

    】:0引言通信软件是船舶导航监控系统的重要组成部分,集数据采集、通信、显示功能于一体,是实现水上智能交通的核心环节[1]。通信软件的实时性、准确性和效率直接影响船舶导航监控系统的性能。
    发表于 05-06 09:02

    LABVIEW如何使用ICMP协议实现多线程ping的功能

    LABVIEW如何通过ICMP协议实现多线程ping的功能?这个网上没查到相关资料。通过cmd同时ping
    发表于 11-16 17:31

    【GD32330C-START开发板申请】基于GD32330C的OLED显示功能实现

    I2C接口的OLED屏为显示器件,显示实现功能。此外,配合RTC和A/D的
    发表于 09-10 09:21

    如何通过ADC实现功能安全的潜力

    本文旨在从确保数据采集系统整体完整性的角度,探讨通过ADC实现功能安全的潜力。
    发表于 01-29 07:20

    Micropython STM32添加多线程功能

    Micropython 官方下载的代码默认未开启多线程支持,可用以下方法: Micropython添加多线程功能的STM32目录下mpconfigport.h中 #define MICROPY_PY_THREAD (1)...
    发表于 08-24 06:08

    micropython esp32刷固件adc多线程如何去实现

    micropython esp32刷固件adc多线程如何去实现呢?
    发表于 01-25 06:09

    NP多线程防火墙的设计与实现

    本文介绍了一种 NP(Network Processor)多线程防火墙的设计方案。首先介绍了NP多线程防火墙的工作原理,然后提出了对该防火墙的体系结构设计,通过对各功能模块的分析,给出
    发表于 08-06 08:25 15次下载

    Delphi教程_实现多线程共享探测

    Delphi教程实现多线程共享探测,很好的Delphi学习资料。
    发表于 03-16 14:59 3次下载

    基于OPC技术的多线程数据采集系统的实现_乔富强

    基于OPC技术的多线程数据采集系统的实现_乔富强
    发表于 01-12 19:56 1次下载

    多线程与聊天室程序的创建

    多线程程序的编写,多线程应用中容易出现的问题。互斥对象的讲解,如何采用互斥对象实现多线程的同步。如何利用命名互斥对象保证应用程序只有一个实
    发表于 05-16 15:22 0次下载

    如何使用LabWindows CVI多线程技术实现实时的数据采集详细资料说明

    LabWindows/CVI软件自带的多线程机制,采用将线程池和异步定时器相互结合的方法实现实时数据采集线程和数据处理
    发表于 07-17 17:02 34次下载
    如何使用LabWindows CVI<b class='flag-5'>多线程</b>技术<b class='flag-5'>实现</b>实时的数据<b class='flag-5'>采集</b>详细资料说明

    如何通过多线程并发设计提高应用程序的性能

    这里我们简单总结了一下,在现代多处理器或多内核环境下,如何通过多线程并发设计提高我们应用程序的性能和响应性。
    的头像 发表于 09-28 02:13 5250次阅读

    SpringBoot实现多线程

    SpringBoot实现多线程
    的头像 发表于 01-12 16:59 1777次阅读
    SpringBoot<b class='flag-5'>实现</b><b class='flag-5'>多线程</b>

    多线程如何保证数据的同步

    。本文将详细介绍多线程数据同步的概念、问题、以及常见的解决方案。 一、多线程数据同步概念 在多线程编程中,数据同步指的是通过某种机制确保多
    的头像 发表于 11-17 14:22 1079次阅读

    java实现多线程的几种方式

    Java实现多线程的几种方式 多线程是指程序中包含了两个或以上的线程,每个线程都可以并行执行不同的任务或操作。Java中的
    的头像 发表于 03-14 16:55 542次阅读