RT-Thread开发学习笔记

一、RT-Thread介绍

1、介绍

RT-Thread全程Real Time-Thread,是一个嵌入式实时多线程操作系统,移植到STM32单片机上。

常见的操作系统:windows、Linux、MAC、安卓操作系统、IOS、鸿蒙操作系统

2、架构

二、RT-Thread移植

  1. 版本

①标准版本

②Nano版本

③Smart版本

  1. 移植标准版本RT-Thread

下载安装RT-Thread_Studio,安装完成后注册并登陆后方可创建RTT工程

  1. 创建项目

  1. 编译下载

3.通过board.h头文件修改SystemClock_Config()更改系统时钟

  1. 打开finshshell组件

三、RT-Thread内核介绍

  1. 内核框架

内核是操作系统最基础也是最重要的部分。下图为 RT-Thread 内核架构图内核处于硬件层之上,内核部分包括内核库、实时内核实现。

  1. 线程调度

线程是 RT-Thread 操作系统中最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。支持 256 个线程优先级(也可通过配置文件更改为最大支持32个或8个线程优先级,针对 STM32默认配置是32个线程优先级),0 优先级代表最高优先级,最低优先级留给空闲线程使用;同时它也支持创建多个具有相同优先级的线程,相同优先级的线程间采用时间片的轮转调度算法进行调度,使每个线程运行相应时间;另外调度器在寻找那些处于就绪状态的具有最高优先级的线程时,所经历的时间是恒定的,系统也不限制线程数量的多少,线程数日只和硬件平台的具体内存相关

  1. 时钟管理

RT-Thread的时钟管理以时钟节拍为基础,时钟节拍是 RT-Thread 操作系统中最小的时钟单位。RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件直到用户手动的停止定时器否则将永远持续执行下去。“另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以设置为HARD TIMER 模式或者 SOFT TIMER 模式。“通常使用定时器定时回调函数(即超时函数),完成定时服务。用户根据自己对定时处理的实时性要求选择合适类型的定时器。

  1. 线程间同步

RT-Thread 采用信号量、互斥量与事件集实现线程间同步。线程通过对信号量、互斥量的获取与释放进行同步:互斥量采用优先级继承的方式解决了实时系统常见的优先级翻转问题。线程同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥量。线程通过对事件的发送与接收进行同步:事件集支持多事件的“或触发”和“与触发”,适合于线程等待多个事件的情况。

  1. 线程间通信

RT-Thread 支持邮箱和消息队列等通信机制。邮箱中一封邮件的长度固定为 4 字节大小:消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。邮箱效率较消息队列更为高效。邮箱和消息队列的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。

  1. 内存管理

RT-Thread 支持静态内存池管理及动态内存堆管理。当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的:当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉(即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。“动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统的内存管理算法及面向大内存系统的 SLAB 内存管理算法。还有一种动态内存堆管理叫做 memheap,适用于系统含有多个地址可不连续的内存堆。使用 memheap 可以将多个内存堆“粘贴”在一起,让用户操作起来像是在操作一个内存堆。

  1. IO设备管理

RT-Thread 将 PINI I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。实现了按名称访问的设备管理子系统,可按照统一的 API界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。

  1. RT-Thread内核启动流程

RT-Thread 支持多种平台和多种编译器,而rtthread startup0函数是RT-Thread规定的统一启动入口。一般执行顺序是: 系统先从启动文件开始运行,然后进入 RT-Thread 的启动 rtthread startup0,最后进入用户入口 main0,如下图所示:

四、线程管理

RT-Thread是支持多任务的操作系统,多任务是通过多线程的方式实现。线程是任务的载体,是RTT中最基本的调度单位。线程在运行的时候,它自己会认为独占CPU 运行线程执行时的运行环境称为上下文,具体来说就是各个变量和数据,包括所有的寄存器变量、堆栈、内存信息等。

  1. 线程管理特点

RT-Thread 线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程和用户线程,系统线程是由RT-Thread 内核创建的线程,用户线程是由

应用程序创建的线程,这两类线程都会从内核对象容器中分配线程对象,当线程被删除时,也会被从对象容器中删除。“RT-Thread的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行最高优先级的任务一旦就绪,总能得到CPU的使用权。

当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时线程调度器将该线程的上下文信息恢复。

  1. 线程控制块

struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object */
rt_uint8_t flags; /**< thread's flags */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; /**< the object list */
rt_list_t tlist; /**< the thread list */
/* stack point and entry */
void *sp; /**< stack point */
void *entry; /**< entry */
void *parameter; /**< parameter */
void *stack_addr; /**< stack address */
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status */
#ifdef RT_USING_SMP
rt_uint8_t bind_cpu; /**< thread is bind to cpu */
rt_uint8_t oncpu; /**< process on cpu` */
rt_uint16_t scheduler_lock_nest; /**< scheduler lock count */
rt_uint16_t cpus_lock_nest; /**< cpus lock count */
rt_uint16_t critical_lock_nest; /**< critical lock count */
#endif /*RT_USING_SMP*/
/* priority */
rt_uint8_t current_priority; /**< current priority */
rt_uint8_t init_priority; /**< initialized priority */
#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
#if defined(RT_USING_SIGNALS)
rt_sigset_t sig_pending; /**< the pending signals */
rt_sigset_t sig_mask; /**< the mask bits of signal */
#ifndef RT_USING_SMP
void *sig_ret; /**< the return stack pointer from signal */
#endif
rt_sighandler_t *sig_vectors; /**< vectors of signal handler */
void *si_list; /**< the signal infor list */
#endif
rt_ubase_t init_tick; /**< thread's initialized tick */
rt_ubase_t remaining_tick; /**< remaining tick */
struct rt_timer thread_timer; /**< built-in thread timer */
void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */
/* light weight process if present */
#ifdef RT_USING_LWP
void *lwp;
#endif
rt_ubase_t user_data; /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;
  1. 线程属性

①线程栈

RT-Thread 线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。

②线程状态

初始状态:当线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状态在RT-Thread 中的宏定义为 RT_THREAD_INIT

就绪状态:在就绪状态下,线程按照优先级排队,等待被执行:一旦当前线程运行完毕让出处理器,操作系统会马上寻找最高优先级的就绪态线程运行。此状态在 RT-Thread 中的宏定义为RT_THREAD_READY

运行状态:线程当前正在运行。在单核系统中,只有 rt_thread selfO 函数返回的线程处于运行状态在多核系统中,可能就不止这一个线处于运行状态。此状态在 RT-Thread 中的宏定义为RT_THREAD_RUNNING

挂起状态:也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RTT_HREAD_SUSPEND

关闭状态:当线程运行结束时将处于关闭状态。关闭状态的线程不参与线程的调度。此状态在态 RT-Thread 中的宏定义为 RT_THREAD_CLOSE

③线程优先级

RT-Thread 最大支持256个线程优先级(0~255),数值越小的优先级越高,0为最高优先级。在一些资源比较紧张的系统中,可以根据实际情况选择只支持 8个或32个优先级的系统配置;对于ARM Cortex-M 系列,普遍采用32个优先级最低优先级默认分配给空闲线程使用,用当前线程将立刻被户一般不使用。在系统中,当有比当前线程优先级更高的线程就绪时,换出,高优先级线程抢占处理器

运行

④时间片

每个线程都有时间片这个参数,但时间片仅对优先级相同的就绪态线程有效。

注意

作为一个实时系统,一个优先级明确的实时系统,如果一介线程中的程序陷入了死循环操作,那么比它优先级低的线程都将不能够得到执行。所以在实时操作系统中必须注意的点就是:线程中不能陷入死循环作,必须要有让出CPU 使用权的动作,如循环中调用延时函数或者主动挂起

  1. 线程状态之间的切换

  1. 系统线程

RT-Thread内核中的系统线程有空闲线程和主线程。

  1. 线程相关操作

线程相关的操作包括: 创建/初始化、启动、运行、删除/脱离。

动态线程与静态线程的区别是:动态线程是系统自动从动态内存堆上分配栈空间与线程句柄(初始化heap 之后才能使用 create 创建动态线程),静态线程是由用户分配栈空间与线程句柄。

①线程创建函数

/**
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the created thread object
*/
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)

②线程删除函数

/**
* This function will delete a thread. The thread object will be removed from
* thread queue and deleted from system object management in the idle thread.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_delete(rt_thread_t thread)

③启动线程函数

/**
* This function will start a thread and put it to system ready queue
*
*@param thread the thread to be startede
*
*@return the operation status, RT_EOK on OK, -RT ERROR on errore
*/
rt_err_t rt_thread_startup(rt_thread_t thread)

④线程初始化函数

/**
* This function will initialize a thread, normally it's used to initialize a
* static thread object.
*
* @param thread the static thread object
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_start the start address of thread stack
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)

⑤获得当前线程

/**
* This function will return self thread object
*
* @return the self thread object
*/
rt_thread_t rt_thread_self(void)

⑥让出处理器资源

/**
* This function will let current thread yield processor, and scheduler will
* choose a highest thread to run. After yield processor, the current thread
* is still in READY state.
*
* @return RT_EOK
*/
rt_err_t rt_thread_yield(void)

⑦线程睡眠

/**
* This function will let current thread delay for some milliseconds.
*
* @param ms the delay ms time
*
* @return RT_EOK
*/
rt_err_t rt_thread_mdelay(rt_int32_t ms)
/**
* This function will let current thread delay for some ticks.
*
* @param tick the delay ticks
*
* @return RT_EOK
*/
rt_err_t rt_thread_delay(rt_tick_t tick)
/**
* This function will let current thread sleep for some ticks.
*
* @param tick the sleep ticks
*
* @return RT_EOK
*/
rt_err_t rt_thread_sleep(rt_tick_t tick)

⑧控制线程函数

/**
* This function will control thread behaviors according to control command.
*
* @param thread the specified thread to be controlled
* @param cmd the control command, which includes
* RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
* RT_THREAD_CTRL_STARTUP for starting a thread;
* RT_THREAD_CTRL_CLOSE for delete a thread;
* RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU.
* @param arg the argument of control command
*
* @return RT_EOK
*/
rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg)

⑨设置和删除idle线程hook函数

/**
* @ingroup Hook
* This function sets a hook function to idle thread loop. When the system performs
* idle loop, this hook function should be invoked.
*
* @param hook the specified hook function
*
* @return RT_EOK: set OK
* -RT_EFULL: hook list is full
*
* @note the hook function must be simple and never be blocked or suspend.
*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void))

/**
* delete the idle hook on hook list
*
* @param hook the specified hook function
*
* @return RT_EOK: delete OK
* -RT_ENOSYS: hook was not found
*/
rt_err_t rt_thread_idle_delhook(void (*hook)(void))

⑩调度器hook函数

/**
* This function will set a hook function, which will be invoked when thread
* switch happens.
*
* @param hook the hook function
*/
void
rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))

代码示例

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-02-06     RT-Thread    first version
 */

#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define LED0_PIN    GET_PIN(B, 8)
#define LED1_PIN    GET_PIN(B, 9)

rt_thread_t th1_obj =NULL;
uint8_t th2_stack[512] = {0};
struct rt_thread th2;



void th1_entry(void *parameter)
{
    int i = 0;
    for (i = 0; i < 5; i++) {
       rt_kprintf("%s",parameter);
       rt_pin_write(LED0_PIN, !rt_pin_read(LED0_PIN));
       rt_thread_mdelay(1000);
    }
}

void th2_entry(void *parameter)
{
    int i = 0;
    for (i = 0; i < 5; i++) {
        rt_kprintf("%s",parameter);
        rt_pin_write(LED1_PIN, !rt_pin_read(LED1_PIN));
        rt_thread_mdelay(1000);
    }
}

void idle_hook(void)
{
    int i = 0;
    while(1){
        if(i == 0)
            rt_kprintf("%s","this is idle_th\r\n");
        i++;
    }
}

void scheduler_hook(struct rt_thread *from, struct rt_thread *to)
{
    rt_kprintf("from:%s->to:%s\r\n",from,to);
}

int main(void)
{


    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);

    th1_obj = rt_thread_create("th1",th1_entry,"this is th1\r\n",1024,20,5);

    if(th1_obj == NULL){
        LOG_E("rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("rt_thread_create successed");

    rt_thread_startup(th1_obj);

    if(rt_thread_init(&th2,"th2",th2_entry,"this is th2\r\n",th2_stack,sizeof(th2_stack),20,5) == -RT_ERROR){
        LOG_E("rt_thread_init failed");
        return -RT_ERROR;
    }
    LOG_D("rt_thread_init successed");

    rt_thread_startup(&th2);

    rt_thread_idle_sethook(idle_hook);

    rt_scheduler_sethook(scheduler_hook);

    while(1){
        rt_thread_mdelay(6000);
        rt_thread_idle_delhook(idle_hook);
    }
    return 0;

}

五、时钟管理

操作系统需要通过时间来规范其任务,本章主要介绍时钟节拍和基于时钟节拍的定时器。

  1. 时钟节拍

任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。

RT-Thread 中,时钟节拍的长度可以根据RT_TICK_PER_SECOND的定义来调整

rtconfig.h 配置文件中定义:

/*
*频率是 1000HZ 周期是 1/1000 s
*所以节拍是 1ms
*/
#define RT_TICK_PER_SECOND 1000

系统滴答定时器中断处理函数(每1ms 触发一次systick 定时器中断):

void SysTick_Handler (void)
{
rt_tick_increase();
//++ rt_tick; 全局变量自加,记录的是系统从启动到现在的时间节拍数
}
  1. 获取系统节拍函数

/**
* This function will return current tick from operating system startup
@return current tick
rt_tick_t rt_tick_get(void)
#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

rt_tick_t current_tick;

int main(void)
{
    int i = 0;
    for (i = 0; i < 5; i++) {
        current_tick = rt_tick_get();
        rt_kprintf("tick = %u\r\n",current_tick);
        rt_thread_mdelay(1000);
    }
    return 0;
}

3.定时器

定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,定时器有硬件定时器和软件定时器之分:

硬件定时器: 芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。

硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。

软件定时器: 由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。

RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS_Tick)的时间长度为单位,即定时数值必须是OS_Tick 的整数倍

①RT-Thread定时器介绍

RT-Thread的定时器提供两类定时器机制:

第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。

第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去。

根据定时器超时函数执行时所处的上下文环境,RT-Thread 的定时器可以分为HARD TIMER 模式和SOFT TIMER 模式。

HARD TIMER模式:中断上下文

定时器超时函数的要求:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等

SOFT_TIMER模式:线程上下文

该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER模式的定时器超时函数在都会在 timer线程的上下文环境中执行

②定时器源码分析

下面以一个例子来说明 RT-Thread 定时器的工作机制。在RT-Thread 定时器模块中维护着两个重要的全局变量:

1、当前系统经过的tick时间 rt_tick (当硬件定时器中断来临时,它将加1) ;

2、定时器链表rt_timer_list。系统新创建并激活的定时器都会按照以超时时间排序的方式插入到rt_timer_list链表中。

如下图所示,系统当前 tick 值为20,在当前系统中已经创建并启动了三个定时器分别是定时时间为 50个tick的Timer1、100个tick的Timer2和500个tick的Timer3这三个定时器分别加上系统,当前时间rt_tick=20,从小到大排序链接在rt_timer_list链表中,形成如图所示的定时器链表结构。

而rt_tick随着硬件定时器的触发一直在增长 (每一次硬件定时器中断来临,rt_tick变量会加 1) ,50个tick 以后,rt_tick 从20增长到70,与Timer1的timeout 值相等这时会触发与Timer1定时器相关联的超时函数,同时将Timer1从rt_timer_list链表上删除。同理,100个 tick 和500个 tick 过去后,与Timer2 和 Timer3 定时器相关联的超时函数会被触发,接着将 Time2 和 Timer3 定时器从rt_timer_list 链表中删除。

如果系统当前定时器状态在10个 tick 以后 (rt_tick=30)有一个任务新创建了一个Ttick 值为 300的Timer4定时器,由于Timer4定时器的timeout=rt tick+300=330,因此它将被插入到 Timer2和Timer3 定时器中间,形成如下图所示链表结构:

  1. 定时器相关API

①动态创建和删除定时器

/**
* This function will create a timer
*
@param name the name of timer
@param timeout the timeout function
@param parameter the parameter of timeout function
@param time the tick of timer
@param flag the flag of timer
*
*@return the created timer object
*/
rt_timer_t rt_timer_create(const char *name,
void(*timeout)(void *parameter),
void*parameter,
rt_tick_t time,
rt_uint8_t flag)

*/
* This function will delete a timer and release timer memory
*
*@param timer the timer to be deletede
*
*@return the operation status, RT_EOK on OK; RT_ERROR on errore
*/
rt_err_t rt_timer_delete(rt_timer_t timer)

②初始化和脱离定时器

* This function will initialize a timer, normally this function is used toe* initialize a static timer object

@param timer the static timer object (typedef struct rt_timer *rt_timer_t;)
@param name the name of timer
@param timeout the timeout function
@param parameter the parameter of timeout function
@param time the tick of timer
@param flag the flag of timer

void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)

* This function will detach a timer from timer management
@param timer the static timer objecte
@return the operation status, RT_EOK on OK; RT_ERROR on error
rt_err_t rt_timer_detach(rt_timer_t timer)

③启动和停止定时器

/**
* This function will start the timere
*
@param timer the timer to be started
*
*@return the operation status, RT_EOK on OK, -RT_ERROR on errore
*
rt_err_t rt_timer_start(rt_timer_t timer)

*
* This function will stop the timer
*
@param timer the timer to be stopped
*
@return the operation status, RT_EOK on OK, -RT_ERROR on error
*
rt_err_t rt_timer_stop(rt_timer_t timer)

④控制定时器

#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

rt_timer_t tm1 = RT_NULL;
struct rt_timer tm2;
int i = 0;

void tm1_function(void *parameter)
{
    rt_tick_t timeout = 5000;
    if(i == 3){
        rt_timer_control(tm1, RT_TIMER_CTRL_SET_ONESHOT, NULL);
    }
    if(i == 1){
        rt_timer_control(tm1, RT_TIMER_CTRL_SET_TIME, (void *)&timeout);
    }
    rt_kprintf("[%u][%d]%s",rt_tick_get(),i,parameter);
    i++;
}

void tm2_function(void *parameter)
{
    rt_kprintf("%s",parameter);
}

int main(void)
{
    tm1 = rt_timer_create("tm1", tm1_function, "this is tm1\r\n", 1000,\
            RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
    if(tm1 == RT_NULL){
        LOG_E("rt_timer_create failed\r\n");
        return -RT_ERROR;
    }
    LOG_D("rt_timer_create successed\r\n");

    rt_timer_start(tm1);

    rt_timer_init(&tm2, "tm2", tm2_function, "this is tm2\r\n", 10000, \
            RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);

    rt_timer_start(&tm2);
    return 0;
}

⑤高精度延时函数

注意: 这个函数只支持低于1个 OS Tick 的延时, 否 SysTick 会出现溢出而不能够获得指定的延时时间

0<=us<=999

* This function will delay for some us.
*
* @param us the delay time of use
*
void rt_hw_us_delay(rt_uint32_t us)

六、线程间同步

多个执行单元(线程、中断)同时执行临界区,操作临界资源,会导致竞态产生,为了解决这种竞态问题,RT-Tread OS 提供了如下几种同步互斥机制:

信号量、互斥量、事件集

  1. 信号量

信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它从而达到同步或互斥的目的。

每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有5个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)

①信号量结构体

struct rt semaphore
{
struct rt_ipc_object parent; /**< inherit from ipc_object 继承自 ipc_object类*/
rt_uint16_t value; /**< value of semaphore. */
rt_uint16_t reserved; /**< reserved field 预留*/
};
typedef struct rt_semaphore *rt_sem_t;

②信号量的使用和管理

对一个信号量的操作包含:创建/初始化信号量、获取信号量、释放信号量、删除/脱离信号量。

③创建/删除信号量函数

/**
* This function will create a semaphore from system resource
*
* @param name the name of semaphore
* @param value the init value of semaphore
* @param flag the flag of semaphore
*
RT_IPC_FLAG_FIFO 按照先进先出 /**< FIFOed IPC. @ref IPC. */
RT_IPC_FLAG_PRIO 按照线程优先级 /**< PRIOed IPC. @ref IPC. */
* @return the created semaphore, RT_NULL on error happen
*
* @see rt_sem_init
*/
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)

/**
* This function will delete a semaphore object and release the memory
*
* @param sem the semaphore object
*
* @return the error code
*
* @see rt_sem_detach
*/
rt_err_t rt_sem_delete(rt_sem_t sem)

④初始化/脱离信号量

/**
* This function will initialize a semaphore and put it under control of
* resource management.
*
* @param sem the semaphore object
* @param name the name of semaphore
* @param value the initial value of semaphore
* @param flag the flag of semaphore
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)

/**
* This function will detach a semaphore from resource management
*
* @param sem the semaphore object
*
* @return the operation status, RT_EOK on successful
*
* @see rt_sem_delete
*/
rt_err_t rt_sem_detach(rt_sem_t sem)

⑤获取释放信号量

/**
* This function will take a semaphore, if the semaphore is unavailable, the
* thread shall wait for a specified time.
*
* @param sem the semaphore object
* @param time the waiting time
*
RT_WAITING_FOREVER -1 阻塞 /**< Block forever until get resource. */
RT_WAITING_NO 0 非阻塞 /**< Non-block. */
*
* @return the error code
*/
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
rt_err_t rt_sem_trytake(rt_sem_t sem) //等价于rt_sem_take(sem, RT_WAITING_NO)

/**
* This function will release a semaphore, if there are threads suspended on
* semaphore, it will be waked up.
*
* @param sem the semaphore object
*
* @return the error code
*/
rt_err_t rt_sem_release(rt_sem_t sem)
#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

rt_sem_t sem1 = RT_NULL;
struct rt_semaphore sem2;
rt_thread_t th1,th2 = RT_NULL;

int flags = 0;

void th1_entry(void *parameter)
{
    while(1){
        rt_thread_mdelay(3000);
        rt_sem_take(sem1, RT_WAITING_FOREVER);
        flags++;
        rt_kprintf("th1:[%d]\r\n",flags);
        rt_sem_release(&sem2);
    }
}
void th2_entry(void *parameter)
{
    while(1){
        rt_sem_take(&sem2, RT_WAITING_FOREVER);
        rt_thread_mdelay(1000);
        flags--;
        rt_kprintf("th2:[%d]\r\n",flags);
        rt_sem_release(sem1);
    }
}
int main(void)
{
    rt_err_t ret;

    sem1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);
    if(sem1 == RT_NULL){
        LOG_E("rt_sem_create failed");
        return -RT_ERROR;
    }
    LOG_D("rt_sem_create successed");

    ret = rt_sem_init(&sem2, "sem2", 0, RT_IPC_FLAG_FIFO);
    if(ret < 0){
        LOG_E("rt_sem_init failed\r\n");
        return ret;
    }
    LOG_D("rt_sem_init successed");

    th1 = rt_thread_create("th1",th1_entry,"this is th1\r\n",1024,20,5);

    if(th1 == NULL){
        LOG_E("th1 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th1 rt_thread_create successed");

    th2 = rt_thread_create("th2",th2_entry,"this is th2\r\n",1024,20,5);

    if(th2 == NULL){
        LOG_E("th2 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th2 rt_thread_create successed");

    rt_thread_startup(th1);
    rt_thread_startup(th2);

    return 0;
}
  1. 互斥量

互斥量体现的是排他性,也是解决多线程同时操作临界区临界资源导致的竟态的一种方法。(类似于特殊的信号量一-二值信号量)

区别: 信号量可由不同线程释放,互斥量只能由同一线程进行释放。

①互斥量的使用和管理

互斥量的操作包括:创建/初始化互斥量,获取互斥量,释放互斥量,删除/脱离互斥量

②互斥量结构体

struct rt_mutex
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of mutex */
rt_uint8_t original_priority; /**< priority of last thread hold the mutex */
rt_uint8_t hold; /**< numbers of thread hold the mutex */
struct rt_thread *owner; /**< current owner of mutex */
};
typedef struct rt_mutex *rt_mutex_t;

③创建/删除互斥量

/**
* This function will create a mutex from system resource
*
* @param name the name of mutex
* @param flag the flag of mutex
*
RT_IPC_FLAG_FIFO 按照先进先出 /**< FIFOed IPC. @ref IPC. */
RT_IPC_FLAG_PRIO 按照线程优先级 /**< PRIOed IPC. @ref IPC. */
* @return the created mutex, RT_NULL on error happen
*
* @see rt_mutex_init
*/
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)

/**
* This function will delete a mutex object and release the memory
*
* @param mutex the mutex object
*
* @return the error code
*
* @see rt_mutex_detach
*/
rt_err_t rt_mutex_delete(rt_mutex_t mutex)

④初始化/脱离互斥量

/**
* This function will initialize a mutex and put it under control of resource
* management.
*
* @param mutex the mutex object
* @param name the name of mutex
* @param flag the flag of mutex
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)

/**
* This function will detach a mutex from resource management
*
* @param mutex the mutex object
*
* @return the operation status, RT_EOK on successful
*
* @see rt_mutex_delete
*/
rt_err_t rt_mutex_detach(rt_mutex_t mutex)

⑤获取/释放互斥量

/**
* This function will take a mutex, if the mutex is unavailable, the
* thread shall wait for a specified time.
*
* @param mutex the mutex object
* @param time the waiting time
RT_WAITING_FOREVER -1 阻塞 /**< Block forever until get resource. */
RT_WAITING_NO 0 非阻塞 /**< Non-block. */
*
* @return the error code
*/
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)

/**
* This function will release a mutex, if there are threads suspended on mutex,
* it will be waked up.
*
* @param mutex the mutex object
*
* @return the error code
*/
rt_err_t rt_mutex_release(rt_mutex_t mutex)
#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

rt_mutex_t mutex1 = RT_NULL;
struct rt_mutex mutex2;
rt_thread_t th1,th2 = RT_NULL;

int flag1,flag2 = 0;

void th1_entry(void *parameter)
{

    while(1){

        rt_mutex_take(mutex1, RT_WAITING_FOREVER);
        flag1++;
        rt_thread_mdelay(1000);
        flag2++;
        rt_mutex_release(mutex1);
    }
}
void th2_entry(void *parameter)
{

    while(1){
        rt_mutex_take(mutex1, RT_WAITING_FOREVER);
        flag1++;
        flag2++;
        rt_mutex_release(mutex1);
        rt_kprintf("flag1[%d]  flag2[%d]\r\n",flag1,flag2);
        rt_thread_mdelay(1000);
    }
}
int main(void)
{
    rt_err_t ret;

    mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);
    if(mutex1 == RT_NULL){
        LOG_E("rt_mutex_create failed");
        return -RT_ERROR;
    }
    LOG_D("rt_mutex_create successed");

    ret = rt_mutex_init(&mutex2, "mutex2", RT_IPC_FLAG_FIFO);
    if(ret < 0){
        LOG_E("rt_mutex_init failed\r\n");
        return ret;
    }
    LOG_D("rt_mutex_init successed");

    th1 = rt_thread_create("th1",th1_entry,"this is th1\r\n",1024,20,5);

    if(th1 == NULL){
        LOG_E("th1 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th1 rt_thread_create successed");

    th2 = rt_thread_create("th2",th2_entry,"this is th2\r\n",1024,20,5);

    if(th2 == NULL){
        LOG_E("th2 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th2 rt_thread_create successed");

    rt_thread_startup(th1);
    rt_thread_startup(th2);

    return 0;
}
  1. 事件集

事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。

一个线程和多个事件的关系可设置为:

其中任意一个事件唤醒 线程,或几个事件都到达后唤醒线程,多个事件集合可以用一个32bit 无符号整型变量来表示,变量的每一位代表一个事件,线程通过”逻辑与"或”逻辑或”将一个或多个事件关联起来,形成事件组合。“

RT-Thread 定义的事件集有以下特点:

1、事件只与线程相关事件间相互独立

2、事件仅用于同步,不提供数据传输功能

3、事件无排队性,即多次向线程发送同一事件(如果线程还未来得及读走),其效果等同于只发送一次

①事件集结构体

struct rt_event
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_uint32_t          set;                           /**< event set */
};
typedef struct rt_event *rt_event_t;

②创建/删除事件集

/**
 * This function will create an event object from system resource
 *
 * @param name the name of event
 * @param flag the flag of event
 *
 * @return the created event, RT_NULL on error happen
 */
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)
/**
 * This function will delete an event object and release the memory
 *
 * @param event the event object
 *
 * @return the error code
 */
rt_err_t rt_event_delete(rt_event_t event)

③初始化/脱离事件集

/**
 * This function will initialize an event and put it under control of resource
 * management.
 *
 * @param event the event object
 * @param name the name of event
 * @param flag the flag of event
 *
 * @return the operation status, RT_EOK on successful
 */
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
/**
 * This function will detach an event object from resource management
 *
 * @param event the event object
 *
 * @return the operation status, RT_EOK on successful
 */
rt_err_t rt_event_detach(rt_event_t event)

④发送/接收事件集

/**
 * This function will send an event to the event object, if there are threads
 * suspended on event object, it will be waked up.
 *
 * @param event the event object
 * @param set the event set
 *
 * @return the error code
 */
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
/**
 * This function will receive an event from event object, if the event is
 * unavailable, the thread shall wait for a specified time.
 *
 * @param event the fast event object
 * @param set the interested event set
 * @param option the receive option, either RT_EVENT_FLAG_AND or
 *        RT_EVENT_FLAG_OR should be set.
 * @param timeout the waiting time
 * @param recved the received event, if you don't care, RT_NULL can be set.
 *
 * @return the error code
 */
rt_err_t rt_event_recv(rt_event_t   event,
                       rt_uint32_t  set,
                       rt_uint8_t   option,
                       rt_int32_t   timeout,
                       rt_uint32_t *recved)

代码示例

#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

rt_event_t event1 = RT_NULL;
struct rt_event event2;
rt_thread_t th1,th2,th3 = RT_NULL;

#define flag1 (0x1<<0)
#define flag2 (0x1<<1)
#define flag3 (0x1<<2)

void th1_entry(void *parameter)
{

    while(1){
        rt_event_recv(event1, flag1, RT_EVENT_FLAG_AND |RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
        rt_thread_mdelay(1000);
        rt_kprintf("th1 running\r\n");
        rt_event_send(event1, flag2);
    }
}
void th2_entry(void *parameter)
{

    while(1){
        rt_event_recv(event1, flag2, RT_EVENT_FLAG_AND |RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
        rt_thread_mdelay(1000);
        rt_kprintf("th2 running\r\n");
        rt_event_send(event1, flag3);
    }
}
void th3_entry(void *parameter)
{

    while(1){
        rt_event_recv(event1, flag3, RT_EVENT_FLAG_AND |RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
        rt_thread_mdelay(1000);
        rt_kprintf("th3 running\r\n");
        rt_event_send(event1, flag1);
    }
}
int main(void)
{
    rt_err_t ret;

    event1 = rt_event_create("event1", RT_IPC_FLAG_FIFO);
    if(event1 == RT_NULL){
        LOG_E("rt_event_create failed");
        return -RT_ERROR;
    }
    LOG_D("rt_event_create successed");

    ret = rt_event_init(&event2, "event2", RT_IPC_FLAG_FIFO);
    if(ret < 0){
        LOG_E("rt_event_init failed");
    }
    LOG_D("rt_event_init successed");



    th1 = rt_thread_create("th1",th1_entry,"this is th1\r\n",1024,20,5);

    if(th1 == NULL){
        LOG_E("th1 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th1 rt_thread_create successed");

    th2 = rt_thread_create("th2",th2_entry,"this is th2\r\n",1024,20,5);

    if(th2 == NULL){
        LOG_E("th2 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th2 rt_thread_create successed");

    th3 = rt_thread_create("th3",th3_entry,"this is th3\r\n",1024,20,5);

    if(th3 == NULL){
        LOG_E("th3 rt_thread_create failed");
        return -RT_ERROR;
    }
    LOG_D("th3 rt_thread_create successed");

    rt_thread_startup(th1);
    rt_thread_startup(th2);
    rt_thread_startup(th3);

    rt_event_send(event1, flag1);

    return 0;
}

七、IO设备模型

IO设备类型

RT-Thread提供了一套简单的IO设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是IO设备管理层、设备驱动框架层、设备驱动层。

1、应用程序通过IO设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层I/O 硬件设备进行交互。

2、IO设备管理层实现了对设备驱动程序的封装

3、设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。

4、设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。

简单设备的注册不经过设备驱动框架层,直接将设备注册到IO设备管理器中

1、设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到IO设备管理器中

2、应用程序通过rt_device_find0接口查找到设备,然后使用IO设备管理接口来访

问硬件,如下图所示:

对于一些复杂设备,需要使用到对应的设备驱动框架层,进行注册,如:看门狗定时器

1、看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过 rt_hw_watchdog_register0接口注册到看门狗设备驱动框架中

2、看门狗设备驱动框架通过 rt_device_register0接口将看门狗设备注册到IO设备管理器中

3、应用程序通过IO设备管理接口来访问看门狗设备硬件

RT-Thread 支持多种I/0设备类型,主要设备类型如下所示

RT Device Class Char /**< character device*/

RT Device Class Block /**< block device*/

RT Device Class Netlf /**< net interface */

RT_Device_Class_MTD /**< memory device*/

RT_Device_Class_CAN /**< CAN device*/

RT_Device_Class RTC /**< RTC device */Real Time Clock设备

RT Device Class Sound /**< Sound device*/

RT Device Class Graphic /**< Graphic device*/图形化设备

RT Device Class I2CBUS /**<I2C bus device*/

RT Device Class USBDevice /**< USB slave device */

RT Device Class USBHost /**< USB host bus*/

RT Device Class SPIBUS /**< SPI bus device*/

RT_Device Class SPIDevice /**< SPI device */

RT Device Class SDIO /**< SDIO bus device */

RT Device Class Timer /**< Timer device*/

RT Device Class Miscellaneous /**< misc device*/杂项设备

RT Device Class Sensor /**< Sensor device*/

RT Device Class Touch /**< Touch device */

RT Device Class Unknown /**< unknown device*/

创建和注册IO设备

驱动层负责创建设备实例,并注册到IO设备管理器中

/**
 * This function creates a device object with user data size.
 *
 * @param type, the kind type of this device object.
 * @param attach_size, the size of user data.
 *
 * @return the allocated device object, or RT_NULL when failed.
 */
rt_device_t rt_device_create(int type, int attach_size)

当动态创建的设备不再需要使用时可以通过如下函数来销毁

/**
 * This function destroy the specific device object.
 *
 * @param dev, the specific device object.
 */
void rt_device_destroy(rt_device_t dev)

设备被创建后,需要实现它访问硬件的操作方法

struct rt_device_ops
{
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};

设备被创建后,需要租车到IO设备管理器中,应用程序才能够访问

/**
 * This function registers a device driver with specified name.
 *
 * @param dev the pointer of device driver structure
 * @param name the device driver's name
 * @param flags the capabilities flag of device设备模式标志
 *
 * @return the error code, RT_EOK on initialization successfully.
 */
rt_err_t rt_device_register(rt_device_t dev,
                            const char *name,
                            rt_uint16_t flags)

#define RT_DEVICE_FLAG_DEACTIVATE       0x000           /**< device is not not initialized */
#define RT_DEVICE_FLAG_RDONLY           0x001           /**< read only */只读
#define RT_DEVICE_FLAG_WRONLY           0x002           /**< write only */只写
#define RT_DEVICE_FLAG_RDWR             0x003           /**< read and write */读写
#define RT_DEVICE_FLAG_REMOVABLE        0x004           /**< removable device */可移除
#define RT_DEVICE_FLAG_STANDALONE       0x008           /**< standalone device */独立
#define RT_DEVICE_FLAG_ACTIVATED        0x010           /**< device is activated */激活
#define RT_DEVICE_FLAG_SUSPENDED        0x020           /**< device is suspended */挂起
#define RT_DEVICE_FLAG_STREAM           0x040           /**< stream mode */流模式
#define RT_DEVICE_FLAG_INT_RX           0x100           /**< INT mode on Rx */中断接收
#define RT_DEVICE_FLAG_DMA_RX           0x200           /**< DMA mode on Rx */DMA接收
#define RT_DEVICE_FLAG_INT_TX           0x400           /**< INT mode on Tx */中断发送
#define RT_DEVICE_FLAG_DMA_TX           0x800           /**< DMA mode on Tx */DMA发送

设备注销后,设备将从设备管理器中移除,也就不能再通过设备查找搜索到该设备。注销设备不会释放设备控制块占用的内存

/**
 * This function removes a previously registered device driver
 *
 * @param dev the pointer of device driver structure
 *
 * @return the error code, RT_EOK on successfully.
 */
rt_err_t rt_device_unregister(rt_device_t dev)

访问IO设备

应用程序通过IO设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件,IO设备管理接口与IO设备的操作方法的映射关系下图所示

①设备对象结构体

struct rt_device
{
    struct rt_object          parent;                   /**< inherit from rt_object */

    enum rt_device_class_type type;                     /**< device type */
    rt_uint16_t               flag;                     /**< device flag */
    rt_uint16_t               open_flag;                /**< device open flag */

    rt_uint8_t                ref_count;                /**< reference count */
    rt_uint8_t                device_id;                /**< 0 - 255 */

    /* device call back */
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

#ifdef RT_USING_DEVICE_OPS
    const struct rt_device_ops *ops;
#else
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
#endif

#if defined(RT_USING_POSIX)
    const struct dfs_file_ops *fops;
    struct rt_wqueue wait_queue;
#endif

    void                     *user_data;                /**< device private data */
};typedef struct rt_device *rt_device_t;

②查找设备

/**
 * This function finds a device driver by specified name.
 *
 * @param name the device driver's name
 *
 * @return the registered device driver on successful, or RT_NULL on failure.
 */
rt_device_t rt_device_find(const char *name)

③初始化设备

/**
 * This function will initialize the specified device
 *
 * @param dev the pointer of device driver structure
 *
 * @return the result
 */
rt_err_t rt_device_init(rt_device_t dev)

④打开关闭设备

/**
 * This function will open a device
 *
 * @param dev the pointer of device driver structure
 * @param oflag the flags for device open
 *
 * @return the result
 */
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)

#define RT_DEVICE_OFLAG_CLOSE           0x000           /**< device is closed */设备已经关闭
#define RT_DEVICE_OFLAG_RDONLY          0x001           /**< read only access */只读方式打开
#define RT_DEVICE_OFLAG_WRONLY          0x002           /**< write only access */只写方式打开
#define RT_DEVICE_OFLAG_RDWR            0x003           /**< read and write */读写方式打开
#define RT_DEVICE_OFLAG_OPEN            0x008           /**< device is opened */设备已经打开
#define RT_DEVICE_OFLAG_MASK            0xf0f           /**< mask of open flag */
#define RT_DEVICE_FLAG_STREAM           0x040           /**< stream mode */流设备打开
#define RT_DEVICE_FLAG_INT_RX           0x100           /**< INT mode on Rx */中断接收
#define RT_DEVICE_FLAG_DMA_RX           0x200           /**< DMA mode on Rx */DMA接收
#define RT_DEVICE_FLAG_INT_TX           0x400           /**< INT mode on Tx */中断发送
#define RT_DEVICE_FLAG_DMA_TX           0x800           /**< DMA mode on Tx */DMA发送

注:RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是“\n”(对应16进制值是0x0A),自动在前面输出一个“\r”(对应16进制值是0x0D)做分行

/**
 * This function will close a device
 *
 * @param dev the pointer of device driver structure
 *
 * @return the result
 */
rt_err_t rt_device_close(rt_device_t dev)

⑤控制设备

/**
 * This function will perform a variety of control functions on devices.
 *
 * @param dev the pointer of device driver structure
 * @param cmd the command sent to device
 * @param arg the argument of command
 *
 * @return the result
 */
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)

#define RT_DEVICE_CTRL_RESUME           0x01            /**< resume device */恢复设备
#define RT_DEVICE_CTRL_SUSPEND          0x02            /**< suspend device */挂起设备
#define RT_DEVICE_CTRL_CONFIG           0x03            /**< configure device */配置设备
#define RT_DEVICE_CTRL_SET_INT          0x10            /**< set interrupt */设置中断
#define RT_DEVICE_CTRL_CLR_INT          0x11            /**< clear interrupt */清理中断
#define RT_DEVICE_CTRL_GET_INT          0x12            /**< get interrupt status */获取中断状态

⑥读写设备

/**
 * This function will read some data from a device.
 *
 * @param dev the pointer of device driver structure
 * @param pos the position of reading
 * @param buffer the data buffer to save read data
 * @param size the size of buffer
 *
 * @return the actually read size on successful, otherwise negative returned.
 *
 * @note since 0.4.0, the unit of size/pos is a block for block device.
 */
rt_size_t rt_device_read(rt_device_t dev,
                         rt_off_t    pos,
                         void       *buffer,
                         rt_size_t   size)
/**
 * This function will write some data to a device.
 *
 * @param dev the pointer of device driver structure
 * @param pos the position of written
 * @param buffer the data buffer to be written to device
 * @param size the size of buffer
 *
 * @return the actually written size on successful, otherwise negative returned.
 *
 * @note since 0.4.0, the unit of size/pos is a block for block device.
 */
rt_size_t rt_device_write(rt_device_t dev,
                          rt_off_t    pos,
                          const void *buffer,
                          rt_size_t   size)

⑦数据收发回调

当硬件设备收到数据时,可以通知如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达

/**
 * This function will set the reception indication callback function. This callback function
 * is invoked when this device receives data.
 *
 * @param dev the pointer of device driver structure
 * @param rx_ind the indication callback function
 *
 * @return RT_EOK
 */
rt_err_t
rt_device_set_rx_indicate(rt_device_t dev,
                          rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))
/**
 * This function will set the indication callback function when device has
 * written data to physical hardware.
 *
 * @param dev the pointer of device driver structure
 * @param tx_done the indication callback function
 *
 * @return RT_EOK
 */
rt_err_t rt_device_set_tx_complete(rt_device_t dev,
                          rt_err_t (*tx_done)(rt_device_t dev, void *buffer))

代码示例

drv_demo.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-02-14     Kevin       the first version
 */

#include <rtdevice.h>

rt_err_t  dev_demo_init(rt_device_t dev)
{
    rt_kprintf("dev_demo_init\r\n");
    return 0;
}
rt_err_t  dev_demo_open(rt_device_t dev, rt_uint16_t oflag)
{
    rt_kprintf("dev_demo_open\r\n");
    return 0;
}
rt_err_t  dev_demo_close(rt_device_t dev)
{
    rt_kprintf("dev_demo_close\r\n");
    return 0;
}
int rt_demo_init(void)
{
    rt_device_t dev_demo = RT_NULL;
    dev_demo = rt_device_create(RT_Device_Class_Char, 32);
    if(dev_demo == RT_NULL){
        return -ENOMEM;
    }
    dev_demo->init = dev_demo_init;
    dev_demo->open = dev_demo_open;
    dev_demo->close = dev_demo_close;

    rt_device_register(dev_demo, "dev_demo", RT_DEVICE_FLAG_WRONLY);
    return 0;
}

INIT_BOARD_EXPORT(rt_demo_init);

main.c

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

int main(void)
{
    rt_device_t dev;
    dev = rt_device_find("dev_demo");
    if(dev == RT_NULL){
        LOG_E("rt_device_find failed");
        return RT_ERROR;
    }
    dev->init(dev);
    dev->open(dev,RT_DEVICE_OFLAG_RDWR);
    dev->close(dev);
    return RT_EOK;

}

八、Uart串口设备

访问串口设备接口

应用程序通过RT-Thread提供的IO设备管理接口来访问串口硬件:

1、查找串口设备 (例:“uart2”)

2、打开串口设备(串口收发数据模式:中断、轮询、DMA)

3、控制串口设备

/**
 * This function will perform a variety of control functions on devices.
 *
 * @param dev the pointer of device driver structure
 * @param cmd the command sent to device
 * @param arg the argument of command
 *
 * @return the result
 */
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)

cmd参数可选:RT_SERIAL_CONFIG

struct serial_configure
{
    rt_uint32_t baud_rate;
    rt_uint32_t data_bits               :4;
    rt_uint32_t stop_bits               :2;
    rt_uint32_t parity                  :2;
    rt_uint32_t bit_order               :1;
    rt_uint32_t invert                  :1;
    rt_uint32_t bufsz                   :16;
    rt_uint32_t reserved                :6;
};
波特率
#define BAUD_RATE_2400                  2400
#define BAUD_RATE_4800                  4800
#define BAUD_RATE_9600                  9600
#define BAUD_RATE_19200                 19200
#define BAUD_RATE_38400                 38400
#define BAUD_RATE_57600                 57600
#define BAUD_RATE_115200                115200
#define BAUD_RATE_230400                230400
#define BAUD_RATE_460800                460800
#define BAUD_RATE_921600                921600
#define BAUD_RATE_2000000               2000000
#define BAUD_RATE_3000000               3000000
数据位
#define DATA_BITS_5                     5
#define DATA_BITS_6                     6
#define DATA_BITS_7                     7
#define DATA_BITS_8                     8
#define DATA_BITS_9                     9
停止位
#define STOP_BITS_1                     0
#define STOP_BITS_2                     1
#define STOP_BITS_3                     2
#define STOP_BITS_4                     3
校验位
#define PARITY_NONE                     0
#define PARITY_ODD                      1
#define PARITY_EVEN                     2
高低位顺序
#define BIT_ORDER_LSB                   0
#define BIT_ORDER_MSB                   1
模式
#define NRZ_NORMAL                      0       /* Non Return to Zero : normal mode */
#define NRZ_INVERTED                    1       /* Non Return to Zero : inverted mode */
接收数据缓存区默认大小
#define RT_SERIAL_RB_BUFSZ              64
RT-Thread提供的默认串口配置
#define RT_SERIAL_CONFIG_DEFAULT           \
{                                          \
    BAUD_RATE_115200, /* 115200 bits/s */  \
    DATA_BITS_8,      /* 8 databits */     \
    STOP_BITS_1,      /* 1 stopbit */      \
    PARITY_NONE,      /* No parity  */     \
    BIT_ORDER_LSB,    /* LSB first sent */ \
    NRZ_NORMAL,       /* Normal mode */    \
    RT_SERIAL_RB_BUFSZ, /* Buffer size */  \
    0                                      \
}

注:缓冲区通过 control 接口修改,缓冲区大小无法动态改变,只有在open 设备之前可以配置。open 设备之后,缓冲区大小不可再进行更改。但除过缓冲区之外的其他参数在open 设备前/后,均可进行更改。

4、发送数据

/**
 * This function will write some data to a device.
 *
 * @param dev the pointer of device driver structure
 * @param pos the position of written
 * @param buffer the data buffer to be written to device
 * @param size the size of buffer
 *
 * @return the actually written size on successful, otherwise negative returned.
 *
 * @note since 0.4.0, the unit of size/pos is a block for block device.
 */
rt_size_t rt_device_write(rt_device_t dev,
                          rt_off_t    pos,
                          const void *buffer,
                          rt_size_t   size)
注意:pos写入数据偏移,此参数串口设备未使用

5、设置发送完成回调函数

在应用程序调用rt_device_write(写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后(例如DMA传送完成或FIFO已经写入完毕产生完成中断时)调用

/**
 * This function will set the indication callback function when device has
 * written data to physical hardware.
 *
 * @param dev the pointer of device driver structure
 * @param tx_done the indication callback function
 *
 * @return RT_EOK
 */
rt_err_t rt_device_set_tx_complete(rt_device_t dev,
                          rt_err_t (*tx_done)(rt_device_t dev, void *buffer))

6、设置接收回调函数

若串口以中断接收模式打开,当串口接收到一个数据产生中断时,就会调用回调函数并且会把此时缓冲区的数据大小放在 size 参数里,把串口设备句柄放在dev 参数里供调用者获取。

若串口以DMA接收模式打开,当DMA 完成一批数据的接收后会调用此回调函数。

/**
 * This function will set the indication callback function when device has
 * written data to physical hardware.
 *
 * @param dev the pointer of device driver structure
 * @param tx_done the indication callback function
 *
 * @return RT_EOK
 */
rt_err_t rt_device_set_tx_complete(rt_device_t dev,
                          rt_err_t (*tx_done)(rt_device_t dev, void *buffer))

注意:一般情况下接收回调函数可以发送一个信号量或者事件通知串口数据处理现场有数据到达

7、接收数据

/**
 * This function will read some data from a device.
 *
 * @param dev the pointer of device driver structure
 * @param pos the position of reading
 * @param buffer the data buffer to save read data
 * @param size the size of buffer
 *
 * @return the actually read size on successful, otherwise negative returned.
 *
 * @note since 0.4.0, the unit of size/pos is a block for block device.
 */
rt_size_t rt_device_read(rt_device_t dev,
                         rt_off_t    pos,
                         void       *buffer,
                         rt_size_t   size)
读取数据偏移量,此参数串口设备未使用

8、关闭串口设备

/**
 * This function will close a device
 *
 * @param dev the pointer of device driver structure
 *
 * @return the result
 */
rt_err_t rt_device_close(rt_device_t dev)

串口实例

1、轮询发送和中断接收

①宏定义结构体使用实例
struct student {
    int  student_age;
    char *student_name;
};

#ifndef xiaoming
#define xiaoming                \
{                               \
    .student_age = 10,          \
    .student_name = "aaaa",     \
}
#endif

int main(void)
{
    static struct student aaa[] ={xiaoming};

    rt_kprintf("name:%s,age:%d\r\n",aaa[0].student_name,aaa[0].student_age);
    return RT_EOK;
}
②#include头文件包含默认路径设置
③代码示例
#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <serial.h>
#include <string.h>
#include <stdlib.h>

rt_err_t ret;
rt_device_t dev_uart2;
struct serial_configure serial_default_config = RT_SERIAL_CONFIG_DEFAULT;
rt_sem_t rx_sem = RT_NULL;
rt_thread_t rx_th = RT_NULL;
char buffer[1024] = {0};

rt_err_t uart2_rx_callback(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(rx_sem);
    return RT_EOK;
}

void rx_th_entry(void *parameter)
{

    int i = 0;
    while(1){
        rt_sem_take(rx_sem, RT_WAITING_FOREVER);
        rt_device_read(dev_uart2, 0, (void *)&buffer[i++], 1);
        if(strstr(buffer,"\r\n") != NULL){
            rt_device_write(dev_uart2, 0, buffer, strlen(buffer));
            if(strstr(buffer,"LED1 OPEN") != NULL){
                rt_device_write(dev_uart2, 0, "LED1 is OPEN\r\n", strlen("LED1 is OPEN\r\n"));
            }
            memset(buffer,'\0',sizeof(buffer));
            i = 0;

            continue;
        }
        if(i >= 30){
            memset(buffer,'\0',sizeof(buffer));
            i = 0;
            continue;
        }
    }
}

int main(void)
{
    dev_uart2 = rt_device_find("uart2");
    if(dev_uart2 == RT_NULL){
        LOG_E("rt_device_find[uart2] failed");
        return RT_ERROR;
    }

    ret = rt_device_open(dev_uart2, RT_DEVICE_OFLAG_RDWR|RT_DEVICE_FLAG_INT_RX);
    if(ret < 0){
        LOG_E("rt_device_open[uart2] failed");
        return ret;
    }

    ret = rt_device_control(dev_uart2, RT_DEVICE_CTRL_CONFIG, (void *)&serial_default_config);
    if(ret < 0){
        LOG_E("rt_device_control[uart2] failed");
        return ret;
    }

    ret = rt_device_set_rx_indicate(dev_uart2, uart2_rx_callback);
    if(ret < 0){
        LOG_E("rt_device_set_rx_indicate[uart2] failed");
        return ret;
    }

    rx_sem = rt_sem_create("rx_sem", 0, RT_IPC_FLAG_FIFO);
    if(rx_sem == RT_NULL){
        LOG_E("rt_sem_create failed");
        return -RT_ERROR;
    }

    rx_th = rt_thread_create("rx_th", rx_th_entry, NULL, 1024, 15, 5);
    if(rx_th == RT_NULL){
        LOG_E("rt_thread_create failed");
        return -RT_ERROR;
    }
    rt_thread_startup(rx_th);

    rt_device_write(dev_uart2, 0, "uart2 config successed...\r\n", sizeof("uart2 config successed...\r\n"));

    while(1){
        rt_device_write(dev_uart2, 0, "uart2 heart pack\r\n", sizeof("uart2 heart pack\r\n"));
        rt_thread_mdelay(5000);
    }
    return RT_EOK;
}

2、轮询发送和DMA接收

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <serial.h>
#include <string.h>
#include <stdlib.h>

rt_err_t ret;
rt_device_t dev_uart2;
struct serial_configure serial_default_config = RT_SERIAL_CONFIG_DEFAULT;
rt_sem_t rx_sem = RT_NULL;
rt_thread_t rx_th = RT_NULL;
char buffer[64] = {0};
rt_size_t rx_len = 0;

rt_err_t uart2_rx_callback(rt_device_t dev, rt_size_t size)
{
    rx_len = size;
    rt_sem_release(rx_sem);
    return RT_EOK;
}

void rx_th_entry(void *parameter)
{

    int i = 0;
    while(1){
        rt_sem_take(rx_sem, RT_WAITING_FOREVER);
        rt_device_read(dev_uart2, 0, buffer, rx_len);
        if(strstr(buffer,"\r\n") != NULL){
            rt_device_write(dev_uart2, 0, buffer, strlen(buffer));
            if(strstr(buffer,"LED1 OPEN") != NULL){
                rt_device_write(dev_uart2, 0, "LED1 is OPEN\r\n", strlen("LED1 is OPEN\r\n"));
            }
            memset(buffer,'\0',sizeof(buffer));
            i = 0;

            continue;
        }
        if(i >= 30){
            memset(buffer,'\0',sizeof(buffer));
            i = 0;
            continue;
        }
    }
}

int main(void)
{
    dev_uart2 = rt_device_find("uart2");
    if(dev_uart2 == RT_NULL){
        LOG_E("rt_device_find[uart2] failed");
        return RT_ERROR;
    }

    ret = rt_device_open(dev_uart2, RT_DEVICE_FLAG_DMA_RX);
    if(ret < 0){
        LOG_E("rt_device_open[uart2] failed %d",ret);
        return ret;

    }

    ret = rt_device_control(dev_uart2, RT_DEVICE_CTRL_CONFIG, (void *)&serial_default_config);
    if(ret < 0){
        LOG_E("rt_device_control[uart2] failed");
        return ret;
    }

    ret = rt_device_set_rx_indicate(dev_uart2, uart2_rx_callback);
    if(ret < 0){
        LOG_E("rt_device_set_rx_indicate[uart2] failed");
        return ret;
    }

    rx_sem = rt_sem_create("rx_sem", 0, RT_IPC_FLAG_FIFO);
    if(rx_sem == RT_NULL){
        LOG_E("rt_sem_create failed");
        return -RT_ERROR;
    }

    rx_th = rt_thread_create("rx_th", rx_th_entry, NULL, 1024, 15, 5);
    if(rx_th == RT_NULL){
        LOG_E("rt_thread_create failed");
        return -RT_ERROR;
    }
    rt_thread_startup(rx_th);

    rt_device_write(dev_uart2, 0, "uart2 config successed...\r\n", sizeof("uart2 config successed...\r\n"));

    while(1){
        rt_device_write(dev_uart2, 0, "uart2 heart pack\r\n", sizeof("uart2 heart pack\r\n"));
        rt_thread_mdelay(5000);
    }
    return RT_EOK;
}

九、ADC设备

ADC(Analog-to-Digital Converter)指模数转换器。是指将连续变化的模拟信号转换

为离散的数字信号的器件。

ADC相关参数说明:

1、分辨率:分辨率以二进制(或十进制)数的位数来表示,一般有8位、10位12位16位等,它说明模数转换器对输入信号的分辨能力,位数越多,表示分辨率越高,恢复模拟信号时会更精确。

2、精度:精度表示ADC器件在所有的数值点上对应的模拟值和真实值之间的最大误差值,也就是输出数值偏离线性最大的距离。

3、转换速率:转换速率是指 A/D 转换器完成一次从模拟到数字的 AD 转换所需时间的倒数。例如某A/D 转换器的转换速率为 1MHZ,则表示完成一次AD 转换时间为1微秒。

访问ADC设备

应用程序通过RT-Thread 提供的ADC设备管理接口来访问ADC硬件相关接口如下所示:

ADC使用流程

代码示例

rt_kprintf()无法打印浮点数解决办法

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <serial.h>
#include <string.h>
#include <stdlib.h>
#include <adc.h>

rt_err_t ret;
rt_adc_device_t dev_adc1;
rt_thread_t th1 = RT_NULL;
rt_uint32_t val = 0;
float val_float = 0;



void th1_entry(void *parameter)
{
    while(1){
        val = rt_adc_read(dev_adc1, 0);
        val_float = (float)val * 3.3/4095;
        rt_kprintf("val:%f\r\n",val_float);
        rt_thread_mdelay(5000);
    }
}

int main(void)
{
    dev_adc1 = (rt_adc_device_t)rt_device_find("adc1");
    if(dev_adc1 == RT_NULL){
        LOG_E("rt_device_find[adc1] failed");
        return -RT_ERROR;
    }
    ret = rt_adc_enable(dev_adc1, 0);
    if(ret < 0 ){
        LOG_E("rt_adc_enable[adc1] failed");
        return ret;
    }

    th1 = rt_thread_create("th1", th1_entry, NULL, 1024, 15, 5);
    if(th1 == RT_NULL){
        LOG_E("rt_thread_create failed");
        return -RT_ERROR;
    }
    rt_thread_startup(th1);

    while(1){
        rt_thread_mdelay(5000);
    }
    return RT_EOK;
}

十、I2C设备

I2C(Inter Integrated Circuit)总线是PHILIPS公司开发的一种半双工、双向二线制同步串行总线

I2C总线传输数据时需要两根信号线:

双向数据线SDA

双向时钟线SCL

电气连接:

代码示例

#define RT_I2C_WR                0x0000
#define RT_I2C_RD               (1u << 0)
#define RT_I2C_ADDR_10BIT       (1u << 2)  /* this is a ten bit chip address */
#define RT_I2C_NO_START         (1u << 4)
#define RT_I2C_IGNORE_NACK      (1u << 5)
#define RT_I2C_NO_READ_ACK      (1u << 6)  /* when I2C reading, we do not ACK */
#define RT_I2C_NO_STOP          (1u << 7)

/*for i2c bus driver*/
struct rt_i2c_bus_device
{
    struct rt_device parent;
    const struct rt_i2c_bus_device_ops *ops;
    rt_uint16_t  flags;
    rt_uint16_t  addr;
    struct rt_mutex lock;
    rt_uint32_t  timeout;
    rt_uint32_t  retries;
    void *priv;
};

struct rt_i2c_bus_device *dev_i2c1 = RT_NULL;
dev_i2c1 = (struct rt_i2c_bus_device *)rt_device_find("i2c1");

rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,
                          struct rt_i2c_msg         msgs[],
                          rt_uint32_t               num);

struct rt_i2c_msg
{
    rt_uint16_t addr;
    rt_uint16_t flags;
    rt_uint16_t len;
    rt_uint8_t  *buf;
};

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <serial.h>
#include <string.h>
#include <stdlib.h>
#include <adc.h>
#include <i2c.h>

rt_err_t ret;
struct rt_i2c_bus_device *dev_i2c1 = RT_NULL;
rt_thread_t th1 = RT_NULL;
struct rt_i2c_msg msg[10];

rt_uint8_t init_buf[27] = {0xAE,0x00,0x10,0x40,0xB0,0x81,0xFF,0xA1,0xA6,0xA8,0x3F,0xC8,0xD3,0x00,0xD5,0x80,0xD8,0x05,0xD9,\
        0xF1,0xDA,0x12,0xDB,0x30,0x8D,0x14,0xAF};





rt_err_t write_cmd(struct rt_i2c_bus_device *bus, rt_uint8_t data)
{
    rt_uint8_t buf[2];
    struct rt_i2c_msg msgs;

    buf[0] = 0x00;
    buf[1] = data;

    msgs.addr = 0x78;
    msgs.flags = RT_I2C_WR|RT_I2C_IGNORE_NACK;
    msgs.buf = buf;
    msgs.len = 2;

    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

rt_err_t write_data(struct rt_i2c_bus_device *bus, rt_uint8_t data)
{
    rt_uint8_t buf[2];
    struct rt_i2c_msg msgs;

    buf[0] = 0x40;
    buf[1] = data;

    msgs.addr = 0x78;
    msgs.flags = RT_I2C_WR|RT_I2C_IGNORE_NACK;
    msgs.buf = buf;
    msgs.len = 2;

    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}



int main(void)
{
    int i;
    dev_i2c1 = (struct rt_i2c_bus_device *)rt_device_find("i2c1");
    if(dev_i2c1 == RT_NULL){
        LOG_E("rt_device_find[i2c1] failed");
        return -RT_ERROR;
    }
    LOG_D("rt_device_find[i2c1] successed\r\n");


    for (i = 0; i < sizeof(init_buf); i++) {
        ret = write_cmd(dev_i2c1,init_buf[i]);
    }

    write_cmd(dev_i2c1,0x00);
    write_cmd(dev_i2c1,0x10);
    write_cmd(dev_i2c1,0xB0);

    write_data(dev_i2c1,0x08);
    write_data(dev_i2c1,0x08);
    ret = write_data(dev_i2c1,0x08);

    rt_kprintf("%d\r\n",ret);


    while(1){
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RT-Thread的menuconfig是一个用于配置RT-Thread内核和组件的工具。通过menuconfig,用户可以方便地选择和配置所需的功能和驱动。menuconfig的配置结果会保存在rtconfig.h文件中,而不是直接修改该文件。这样做的好处是,在重新运行menuconfig时,之前的配置不会被覆盖。rtconfig.h文件中的宏可以作为功能和文件的编译开关,用于控制代码的编译。例如,可以使用config JSFW_USING_HWS_CMD bool "HWS : Enable hws test cmd" default n来定义一个名为JSFW_USING_HWS_CMD的宏,并设置其默认值为n。这样,在编译时可以根据该宏的值来决定是否启用相应的功能。\[1\]\[3\] #### 引用[.reference_title] - *1* *3* [RT-Thread 入门学习笔记 - menuconfig Kconfig的使用](https://blog.csdn.net/tcjy1000/article/details/111567253)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [RT-thread 添加 Env 环境 和添加menuconfig功能,修改文件结构](https://blog.csdn.net/qq50031185/article/details/112310748)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值