• 潘多拉 IOT 开发板学习(RT-Thread)—— 实验1 LED 闪烁实验(学习笔记)


    本文代码参考 RT-Thread 官方 BSP

    实验功能

    例程源码:(main.c)

    该实验实现了 LED_R 的闪烁,亮灭间隔为 500 ms。

    /*
     * Copyright (c) 2006-2018, RT-Thread Development Team
     *
     * SPDX-License-Identifier: Apache-2.0
     *
     * Change Logs:
     * Date           Author       Notes
     * 2018-08-16     armink       first implementation
     */
    
    #include <rtthread.h>
    #include <rtdevice.h>
    #include <board.h>
    
    #define DBG_TAG "main"
    #define DBG_LVL         DBG_LOG
    #include <rtdbg.h>
    
    /* 配置 LED 灯引脚 */
    #define LED_PIN              PIN_LED_R
    
    int main(void)
    {
        unsigned int count = 1;
    
        /* 设置 LED 引脚为输出模式 */
        rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
    
        while (count > 0)
        {
            /* LED 灯亮 */
            rt_pin_write(LED_PIN, PIN_LOW);
            LOG_D("led on, count: %d", count);
            rt_thread_mdelay(500);
    
            /* LED 灯灭 */
            rt_pin_write(LED_PIN, PIN_HIGH);
            LOG_D("led off");
            rt_thread_mdelay(500);
    
            count++;
        }
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    代码剖析

    rt_pin_mode()

    该函数的作用是 GPIO Pin 的初始化,定义为

    /* RT-Thread Hardware PIN APIs */
    void rt_pin_mode(rt_base_t pin, rt_base_t mode)
    {
        RT_ASSERT(_hw_pin.ops != RT_NULL);
        _hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参数 pin 是一个 rt_base_t 变量(long),下面的 GET_PIN() 是 STM32 的 pin 值宏定义,第一个参数填大写字母,第二个参数填数字。

    #define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA)/(0x0400UL) )) + PIN)
    
    #define __STM32_PORT(port)  GPIO##port   // ## 是字符连接符,假如 port 为 A,则表示 GPIOA
    
    • 1
    • 2
    • 3

    例如实验中的 #define PIN_LED_R GET_PIN(E, 7) ,表示 GPIOE GPIO_Pin7

    目前 RT-Thread 支持的引脚工作模式包括:

    #define PIN_MODE_OUTPUT 0x00            /* 输出 */
    #define PIN_MODE_INPUT 0x01             /* 输入 */
    #define PIN_MODE_INPUT_PULLUP 0x02      /* 上拉输入 */
    #define PIN_MODE_INPUT_PULLDOWN 0x03    /* 下拉输入 */
    #define PIN_MODE_OUTPUT_OD 0x04         /* 开漏输出 */
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在 bsp 的 drv_gpio.c 文件中,有底层 GPIO 驱动,下面是 STM32 的 GPIO 模式设置的驱动函数(大家应该很熟悉,就是用 HAL 库写的 GPIO 初始化代码)

    static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
    {
        const struct pin_index *index;
        GPIO_InitTypeDef GPIO_InitStruct;
    
        index = get_pin(pin);
        if (index == RT_NULL)
        {
            return;
        }
    
        /* Configure GPIO_InitStructure */
        GPIO_InitStruct.Pin = index->pin;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    
        if (mode == PIN_MODE_OUTPUT)
        {
            /* output setting */
            GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
        }
        else if (mode == PIN_MODE_INPUT)
        {
            /* input setting: not pull. */
            GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
        }
        else if (mode == PIN_MODE_INPUT_PULLUP)
        {
            /* input setting: pull up. */
            GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
            GPIO_InitStruct.Pull = GPIO_PULLUP;
        }
        else if (mode == PIN_MODE_INPUT_PULLDOWN)
        {
            /* input setting: pull down. */
            GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
            GPIO_InitStruct.Pull = GPIO_PULLDOWN;
        }
        else if (mode == PIN_MODE_OUTPUT_OD)
        {
            /* output setting: od. */
            GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
        }
    
        HAL_GPIO_Init(index->gpio, &GPIO_InitStruct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    rt_pin_write()

    GPIO 写函数,下面是函数的定义,

    void rt_pin_write(rt_base_t pin, rt_base_t value)
    {
        RT_ASSERT(_hw_pin.ops != RT_NULL);
        _hw_pin.ops->pin_write(&_hw_pin.parent, pin, value);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    和 GPIO 模式配置函数类似,它其实也会调用底层驱动里对应的函数,该底层函数是通过 HAL_GPIO_WritePin() 来完成 GPIO Pin 的修改。

    static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
    {
        const struct pin_index *index;
    
        index = get_pin(pin);
        if (index == RT_NULL)
        {
            return;
        }
    
        HAL_GPIO_WritePin(index->gpio, index->pin, (GPIO_PinState)value);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    rt_thread_mdelay()

    这是 RT-Thread 的毫秒级延时函数,定义如下:

    rt_err_t rt_thread_mdelay(rt_int32_t ms)
    {
        rt_tick_t tick;
    
    	// 获取需要的时钟节拍
        tick = rt_tick_from_millisecond(ms);
    	
    	// 阻塞相应的节拍时间
        return rt_thread_sleep(tick);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    rt_tick_from_millisecond()

    
    /**
     * 算出 ms 对应的时钟节拍数
     * 
     *
     * @param ms the specified millisecond
     *           - Negative Number wait forever
     *           - Zero not wait
     *           - Max 0x7fffffff
     *
     * @return the calculated tick
     */
    rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
    {
        rt_tick_t tick;
    
        if (ms < 0)
        {
            tick = (rt_tick_t)RT_WAITING_FOREVER;  // -1 
        }
        else
        {
        	// 将“每秒节拍数” / 1000 * ms,算出对应的秒节拍数
            tick = RT_TICK_PER_SECOND * (ms / 1000);
    		
    		// 加上小于 1000ms 部分的节拍数
            tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
        }
        
        /* return the calculated tick */
        return tick;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    rt_thread_sleep()

    线程睡眠(挂起)函数,参数是系统节拍数:

    /**
     * 该函数能让当前线程挂起一段时间(由 tick 决定)
     *
     * @param tick the sleep ticks
     *
     * @return RT_EOK
     */
    rt_err_t rt_thread_sleep(rt_tick_t tick)
    {
        register rt_base_t temp;
        struct rt_thread *thread;
    
        /* set to current thread */
        thread = rt_thread_self();
        RT_ASSERT(thread != RT_NULL);
        RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
    
        /* disable interrupt */
        temp = rt_hw_interrupt_disable();
    
        /* suspend thread */
        rt_thread_suspend(thread);
    
        /* reset the timeout of thread timer and start it */
        rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
        rt_timer_start(&(thread->thread_timer));
    
        /* enable interrupt */
        rt_hw_interrupt_enable(temp);
    
        rt_schedule();
    
        /* clear error number of this thread to RT_EOK */
        if (thread->error == -RT_ETIMEOUT)
            thread->error = RT_EOK;
    
        return RT_EOK;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    LOG_D()

    本实验中,我们可以将 LOG_D() 视为 rt_kprintf()

    #define dbg_log_line(lvl, color_n, fmt, ...)                \
        do                                                      \
        {                                                       \
            _DBG_LOG_HDR(lvl, color_n);                         \
            rt_kprintf(fmt, ##__VA_ARGS__);                     \
            _DBG_LOG_X_END;                                     \
        }                                                       \
        while (0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    LOG_D 是 RT-Thread 内核里的一个日志打印函数,详情可见:《RT-Thread 文档中心——ulog 日志》

    RT-Thread 的日志 API 包括:

    在这里插入图片描述

  • 相关阅读:
    05-前端基础CSS第三天
    JAVA案例驱动模式
    .net 温故知新:Asp.Net Core WebAPI 入门使用及介绍
    crossover23.6闪亮登场发布啦,2023最新功能解析
    【唯美情侣爱情表白纪念HTML单页】
    2022“杭电杯” 中国大学生算法设计超级联赛(3)2 9题解
    向量数据库——AI时代的基座
    MongoDB教程(二):mongoDB引用shell
    1483. 树节点的第 K 个祖先 折半/倍增
    【elementui源码解析】如何实现自动渲染md文档-第一篇
  • 原文地址:https://blog.csdn.net/weixin_43772810/article/details/125533951