sc, value);
else
gpiod_set_raw_value_commit(desc, value);
}
所以我们在设备树中配置引脚的正负逻辑,会在这里被处理。如果是正逻辑,直接输出,如果是负逻辑,则需要取反。
配置引脚正负逻辑
为了知道我们到底应该在哪些引脚配置我们的正负逻辑,我们应该直接看源代码,因为压根就没有文档说这些。
static void fbtft_reset(struct fbtft_par *par)
{
if (!par->gpio.reset)
return;
fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__);
gpiod_set_value_cansleep(par->gpio.reset, 1);
usleep_range(20, 40);
gpiod_set_value_cansleep(par->gpio.reset, 0);
msleep(120);
gpiod_set_value_cansleep(par->gpio.cs, 1); /* Activate chip */
}
RESET引脚在低电平的时候将会导致屏幕复位,Chip-Select在低电平时将会使能设备。所以这两个引脚都是负逻辑。我们应当在他们设备树的GPIO后边加一个GPIO_ACTIVE_LOW
。
上电测试
我上电后,屏幕一直白屏,调试了很久,代码改来改去,TF卡不知道插拔了多少次,也找不到原因。
最后插上逻辑分析仪,逻辑分析仪真的是电子工程师的眼睛,以前屏幕从来就没调通过,这次插了逻辑分析仪,能够分析逻辑后,调试就变得很容易了。一直看波形,发现RESET引脚一直被拉低。才发现原来linux的gpio子系统有这么一套正负逻辑的区别。
配置好之后,屏幕时好时坏。有多时好时坏呢,一插上逻辑分析仪,屏幕就成功初始化。一拔掉逻辑分析仪,这个屏幕又白屏了。最后一联想,就想到逻辑分析仪不是自带上拉吗?是不是因为我的RESET,CS,MOSI,CLK这些引脚都没有上拉?找了两个上拉电阻,一试,屏幕跑起来了!小企鹅出现了!
背光驱动的修改
其实我一开始把PE4的一个引脚设置为背光引脚(这样做似乎不太对,应该用mosfet或者三极管来驱动这个tft,因为电流大了会把f1c100s的引脚给烧了)。但是这个屏幕不知道为什么,就是不亮,亮都不亮一下。于是我就把这根线拔了,直接把TFT的LED引脚连了3V3。但是我觉得这样不够优雅,一定要让linux自己能够设置这个背光。
backlight: backlight {
compatible = "gpio-backlight";
gpios = <&pio 4 4 GPIO_ACTIVE_HIGH>;
default-on;
};
这个backlight会注册一个class,位置在/sys/class/backlight/backlight
,这个目录下有一个文件叫brightness
,即亮度,可以通过echo 1 > brightness
,打开我们的背光。
这里我们把gpio配置为正逻辑,这样1就对应着打开背光,符合人类的操作逻辑。
但是为什么,这个背光不工作呢?设置成1或者0,在调试后,发现
static int gpio_backlight_update_status(struct backlight_device *bl)
{
struct gpio_backlight *gbl = bl_get_data(bl);
int brightness = bl->props.brightness;
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
gpiod_set_value_cansleep(gbl->gpiod, brightness);
return 0;
}
是它判断这些power fb_blakn state
的时候,直接把brightness设置成0了。我们直接去掉这几行代码。但是它调用gpiod_set_value_cansleep(gbl->gpiod, brightness);
,我们的gpio也没有输出。
最后看了几遍代码,发现原来是它没有配置好gpio为输出。
static int gpio_backlight_probe_dt(struct platform_device *pdev,
struct gpio_backlight *gbl)
{
struct device *dev = &pdev->dev;
int ret;
gbl->def_value = device_property_read_bool(dev, "default-on");
gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS);
if (IS_ERR(gbl->gpiod)) {
ret = PTR_ERR(gbl->gpiod);
if (ret != -EPROBE_DEFER) {
dev_err(dev,
"Error: The gpios parameter is missing or invalid.\n");
}
return ret;
}
return 0;
}
问题就在gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS);
的这个GPIOD_ASIS
。
/**
* enum gpiod_flags - Optional flags that can be passed to one of gpiod_* to
* configure direction and output value. These values
* cannot be OR'd.
*
* @GPIOD_ASIS: Don't change anything
* @GPIOD_IN: Set lines to input mode
* @GPIOD_OUT_LOW: Set lines to output and drive them low
* @GPIOD_OUT_HIGH: Set lines to output and drive them high
* @GPIOD_OUT_LOW_OPEN_DRAIN: Set lines to open-drain output and drive them low
* @GPIOD_OUT_HIGH_OPEN_DRAIN: Set lines to open-drain output and drive them high
*/
enum gpiod_flags {
GPIOD_ASIS = 0,
GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET,
GPIOD_OUT