针对RT_Thread中rt_pin_mode()函数的分析

针对RT_Thread中rt_pin_mode()函数的分析


前言

最近学习RT_Thread系统,和以往学单片机一样,依旧是以流水灯做为第一份学习案例;学硬件的基本都有个习惯,看代码喜欢扣到底层,例程中的流水灯IO口配置是用的封装好的函数,所以我就想看看函数底层是如何实现配置GPIO模式以及执行控制IO操作的。


提示:以下是本篇文章正文内容,下面案例可供参考

一、流水灯代码

该应用代码来自于RT-Thread,此代码只有应用层的程序,底层的驱动程序可以在RT-Thread网站下载完整的发布版本RT-Thread完整版,感谢前辈们的开源资料

整体思路很容易理解:
1.配置LED_PIN的GPIO模式为推挽输出模式

2.在for循环中,设置LED_PIN的输出电平,使IO输出 电压在高低电平间进行切换,从而实现流水灯的视觉效果。
其中用了rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT)以及rt_pin_write(LED_PIN, PIN_LOW)俩个封装好的函数,后面会对这两个函数原理进行分析。

int led(void)
{
    rt_uint8_t count;

    rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);  
    
    for(count = 0 ; count < 10 ;count++)
    {       
        rt_pin_write(LED_PIN, PIN_HIGH);
        rt_kprintf("led on, count : %d\r\n", count);
        rt_thread_mdelay(500);
        
        rt_pin_write(LED_PIN, PIN_LOW);
        rt_kprintf("led off\r\n");
        rt_thread_mdelay(500);
    }
    return 0;
}

二、分析void rt_pin_mode(rt_base_t pin, rt_base_t mode)函数

1.查看其函数原型

代码如下(示例):

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

其中RT_ASSERT是很个有用的报错函数,当该处出现错误时会把出错的函数,行数这些信息打出来,便于调试bug。

_hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
可以猜到该行代码就是用来配置GPIO的模式,根据传下来的pin,mode进行相应的配置;接下来我们看下pin_mode这个接口是如何实现的。

2.pin_mode函数原型分析

代码如下:

static void pin_mode(struct rt_device *dev, rt_base_t pin, rt_base_t mode)
{
    if ((pin > PIN_NUM(pin_index)) || (pin_index[pin].magic != PIN_MAGIC))
    {
        LOG_E("pin:%d value wrongful", pin);
        return;
    }

    gpio_set_func(pin_index[pin].pin_port, pin_index[pin].pin, mode);
}
int gpio_set_func(enum gpio_port port, enum gpio_pin pin, rt_uint8_t func)
{
    rt_uint32_t addr;
    rt_uint32_t offset;
    rt_uint32_t data;

    RT_ASSERT((GPIO_PORT_A <= port) && (port < GPIO_PORT_NUM));
    RT_ASSERT((GPIO_PIN_0 <= pin) && (pin < GPIO_PIN_NUM));

    if (func & 0x8)
    {
        LOG_W("[line]:%d There is a warning with parameter input", __LINE__);
        return RT_EINVAL;
    }

    addr = GPIOn_CFG_ADDR(port) + (pin / 8) * 4;
    offset = (pin % 8) * 4;

    data = readl(addr);
    data &= ~(0x7 << offset);
    data |= func << offset;
    writel(data, addr);

    LOG_D("[line]:%d offset:%d addr:%08x data:%08x", __LINE__, offset, addr, *((rt_uint32_t *)addr));
    return RT_EOK;
}

可以看出是通过gpio_set_func()函数,根据传入的pin口和mode参数对寄存器赋相应的值,从而实现相应的GPIO的模式配置


## 3.rt_pin_write函数原型分析 代码如下:
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);
}

static void pin_write(struct rt_device *dev, rt_base_t pin, rt_base_t value)
{
    if ((pin > PIN_NUM(pin_index)) || (pin_index[pin].magic != PIN_MAGIC))
    {
        LOG_E("pin:%d value wrongful", pin);
        return;
    }

    gpio_set_value(pin_index[pin].pin_port, pin_index[pin].pin, value);
}
int gpio_set_value(enum gpio_port port, enum gpio_pin pin, rt_uint8_t value)
{
    rt_uint32_t addr;
    rt_uint32_t offset;
    rt_uint32_t data;

    RT_ASSERT((GPIO_PORT_A <= port) && (port < GPIO_PORT_NUM));
    RT_ASSERT((GPIO_PIN_0 <= pin) && (pin < GPIO_PIN_NUM));

    if (value & 0xE)
    {
        LOG_W("[line]:%d There is a warning with parameter input", __LINE__);
        return RT_EINVAL;
    }

    addr = GPIOn_DATA_ADDR(port);
    offset = pin;

    data = readl(addr);
    data &= ~(0x1 << offset);
    data |= value << offset;
    writel(data, addr);

    LOG_D("[line]:%d offset:%d addr:%08x data:%08x", __LINE__, offset, addr, *((rt_uint32_t *)addr));
    return RT_EOK;
}

可以看出是对GPIO进行写操作是经过pin_write()调用gpio_set_value()函数,从而实现对底层的操作寄存器进行赋值,使GPIO按照预期输出高低电平,从而驱动led的亮和灭,达到流水灯的效果

总结

由于个人水平有限,如有疏漏请大家指出。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个基于 RT-Thread 平台的超声波测距的示例代码,可以作为参考: ```c #include <rtthread.h> #include <rtdevice.h> #define TRIG_PIN GET_PIN(B, 8) #define ECHO_PIN GET_PIN(B, 9) static rt_device_t dev; static rt_thread_t thread; static void ultrasonic_task(void *parameter) { rt_uint32_t start_tick, end_tick; rt_uint32_t distance; while (1) { /* 产生 10us 的高电平脉冲,触发超声波模块发射脉冲 */ rt_pin_write(TRIG_PIN, PIN_LOW); rt_thread_delay_us(2); rt_pin_write(TRIG_PIN, PIN_HIGH); rt_thread_delay_us(10); rt_pin_write(TRIG_PIN, PIN_LOW); /* 等待超声波模块返回的反射信号 */ while (rt_pin_read(ECHO_PIN) == PIN_LOW); start_tick = rt_tick_get(); while (rt_pin_read(ECHO_PIN) == PIN_HIGH); end_tick = rt_tick_get(); /* 计算距离(单位:mm) */ distance = (end_tick - start_tick) * 340 / 2000; /* 打印测量结果 */ rt_kprintf("Distance: %dmm\n", distance); /* 间隔 500ms 进行下一次测量 */ rt_thread_mdelay(500); } } int ultrasonic_init(void) { /* 配置 TRIG 引脚为输出模式 */ rt_pin_mode(TRIG_PIN, PIN_MODE_OUTPUT); /* 配置 ECHO 引脚为输入模式 */ rt_pin_mode(ECHO_PIN, PIN_MODE_INPUT); /* 创建线程 */ thread = rt_thread_create("ultrasonic", ultrasonic_task, RT_NULL, 1024, 25, 10); if (thread != RT_NULL) { rt_thread_startup(thread); } return 0; } ``` 该代码主要使用了 RT-Thread 的定时器和 GPIO 模块来实现,其使用 `rt_pin_write()` 函数产生 10us 的高电平脉冲,触发超声波模块发射脉冲;使用 `rt_pin_read()` 函数检测反射信号,并计算出测量距离。同时,为了避免主线程阻塞,使用了一个单独的线程来执行测距任务。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值