针对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的亮和灭,达到流水灯的效果
总结
由于个人水平有限,如有疏漏请大家指出。