树莓派-LED篇

1 leds驱动

新增文件:arch\arm\boot\dts\overlays\example-overlay.dts

/dts-v1/;

/plugin/;

 

/{

compatible = "brcm,bcm2835";

 

fragment@0 {

target = <&gpio>;

__overlay__ {

leds_pins: leds_pins {

brcm,pins = <25>;

brcm,function = <1>; /* out */

};

};

};

 

fragment@1 {

target = <&i2c_arm>;

__overlay__ {

#address-cells = <1>;

#size-cells = <0>;

status = "okay";

 

aw9523b: aw9523b@5b {

compatible = "xxx,aw9523b-leds";

reg = <0x5b>;

pinctrl-names = "default";

pinctrl-0 = <&leds_pins>;

reset-gpios = <&gpio 25 0>;

status = "okay";

};

};

};

 

__overrides__ {

aw9523b = <&aw9523b>,"status";

};

};

 

linux\arch\arm\boot\dts\overlays\Makefile

dtbo-$(RPI_DT_OVERLAYS) += example-leds.dtbo

 

 

linux\drivers\leds\leds-aw9523b.c

/*

 * TI LP8860 4-Channel LED Driver

 *

 * Copyright (C) 2014 Texas Instruments

 *

 * Author: Dan Murphy <dmurphy@ti.com>

 *

 * This program is free software; you can redistribute it and/or

 * modify it under the terms of the GNU General Public License

 * version 2 as published by the Free Software Foundation.

 *

 */

 

#include <linux/i2c.h>

#include <linux/init.h>

#include <linux/leds.h>

#include <linux/module.h>

#include <linux/mutex.h>

#include <linux/of.h>

#include <linux/of_gpio.h>

#include <linux/gpio.h>

#include <linux/slab.h>

#include <linux/delay.h>

 

 

#define AW9523B_REG_CHIPID 0x10

#define AW9523B_CHIPID_VALUE 0x23

#define AW9523B_LED_OFFSET 3

#define AW9523B_COLOR_OFFSET 3

 

static struct mutex aw9523b_mutex;

 

enum led_ids {

LED1,

LED2,

LED3,

LED_NUM,

};

 

enum led_colors {

LED1_RED = 0,

LED1_GREEN = 1,

LED1_BLUE = 2,

LED2_RED = 3,

LED2_GREEN = 4,

LED2_BLUE = 5,

LED3_RED = 6,

LED3_GREEN = 7,

LED3_BLUE = 8,

};

 

struct aw9523b_led {

struct aw9523b_led_platform_data *pdata;

struct i2c_client *client;

struct rw_semaphore rwsem;

struct work_struct work;

 

/*

 * Making led_classdev as array is not recommended, because array

 * members prevent using 'container_of' macro. So repetitive works

 * are needed.

 */

struct led_classdev cdev_led1r;

struct led_classdev cdev_led1g;

struct led_classdev cdev_led1b;

 

struct led_classdev cdev_led2r;

struct led_classdev cdev_led2g;

struct led_classdev cdev_led2b;

 

struct led_classdev cdev_led3r;

struct led_classdev cdev_led3g;

struct led_classdev cdev_led3b;

 

 

enum led_ids led_id;

enum led_colors color;

enum led_brightness brightness;

 

int reset_gpio;

};

 

static inline u8 aw9523b_get_base_offset(enum led_ids id, enum led_colors color)

{

return id * AW9523B_LED_OFFSET + color;

}

 

static inline u8 aw9523b_get_reg_addr(enum led_ids id, enum led_colors color,

u8 reg_offset)

{

return reg_offset + aw9523b_get_base_offset(id, color);

}

 

 

/*--------------------------------------------------------------*/

/* AW9523BGU core functions */

/*--------------------------------------------------------------*/

 

static int aw9523b_write_byte(struct i2c_client *client, u8 reg, u8 val)

{

int ret;

 

mutex_lock(&aw9523b_mutex);

ret = i2c_smbus_write_byte_data(client, reg, val);

 

if (ret >= 0)

{

mutex_unlock(&aw9523b_mutex);

return 0;

}

 

dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",

__func__, reg, val, ret);

mutex_unlock(&aw9523b_mutex);

 

return ret;

}

 

static int aw9523b_read_byte(struct i2c_client *client, u8 reg, u8 *val)

{

int ret;

 

mutex_lock(&aw9523b_mutex);

ret = i2c_smbus_read_byte_data(client, reg);

if (ret < 0)

{

mutex_unlock(&aw9523b_mutex);

return -EIO;

}

 

*val = ret;

mutex_unlock(&aw9523b_mutex);

return 0;

}

 

static void aw9523b_reset(struct i2c_client *client)

{

struct aw9523b_led *led = i2c_get_clientdata(client);

/* Configure RESET GPIO (L: RESET, H: RESET cancel) */

gpio_direction_output(led->reset_gpio, 0);

 

/* Tacss = min 0.1ms */

udelay(20);

gpio_direction_output(led->reset_gpio, 1);

udelay(100);

 

aw9523b_write_byte(client,0x12,0x00);   //OUT4~9配置为呼吸灯模式

aw9523b_write_byte(client,0x12,0x00);   //OUT4~9配置为呼吸灯模式  

aw9523b_write_byte(client,0x13,0x00);   //OUT0~3配置为呼吸灯模式

aw9523b_write_byte(client,0x13,0x00);   //OUT0~3配置为呼吸灯模式

}

static void aw9523b_set_brightness(struct aw9523b_led *led, enum led_ids id,

enum led_colors color, enum led_brightness brightness)

{

u8 reg;

int ret;

 

reg = aw9523b_get_reg_addr(id, color, 0x20);

ret = aw9523b_write_byte(led->client, reg, brightness);

if(ret <0)

aw9523b_reset(led->client);

}

 

#define AW9523B_SET_REGISTER(reg_addr, reg_name) \

static ssize_t aw9523b_store_reg##reg_addr(struct device *dev, \

struct device_attribute *attr, const char *buf, size_t count) \

{ \

struct aw9523b_led *led = i2c_get_clientdata(to_i2c_client(dev));\

unsigned long val; \

int ret; \

if (!count) \

return -EINVAL; \

ret = kstrtoul(buf, 16, &val); \

if (ret) \

return ret; \

down_write(&led->rwsem); \

aw9523b_write_byte(led->client, reg_addr, (u8) val); \

up_write(&led->rwsem); \

return count; \

} \

static struct device_attribute aw9523b_reg##reg_addr##_attr = { \

.attr = {.name = reg_name, .mode = 0644}, \

.store = aw9523b_store_reg##reg_addr, \

};

 

AW9523B_SET_REGISTER(0x20, "0x20");

AW9523B_SET_REGISTER(0x21, "0x21");

AW9523B_SET_REGISTER(0x22, "0x22");

AW9523B_SET_REGISTER(0x23, "0x23");

AW9523B_SET_REGISTER(0x24, "0x24");

AW9523B_SET_REGISTER(0x25, "0x25");

AW9523B_SET_REGISTER(0x26, "0x26");

AW9523B_SET_REGISTER(0x27, "0x27");

AW9523B_SET_REGISTER(0x28, "0x28");

AW9523B_SET_REGISTER(0x29, "0x29");

AW9523B_SET_REGISTER(0x2a, "0x2a");

AW9523B_SET_REGISTER(0x2b, "0x2b");

AW9523B_SET_REGISTER(0x2c, "0x2c");

AW9523B_SET_REGISTER(0x2d, "0x2d");

AW9523B_SET_REGISTER(0x2e, "0x2e");

AW9523B_SET_REGISTER(0x2f, "0x2f");

 

static struct device_attribute *aw9523b_addr_attributes[] = {

&aw9523b_reg0x20_attr,

&aw9523b_reg0x21_attr,

&aw9523b_reg0x22_attr,

&aw9523b_reg0x23_attr,

&aw9523b_reg0x24_attr,

&aw9523b_reg0x25_attr,

&aw9523b_reg0x26_attr,

&aw9523b_reg0x27_attr,

&aw9523b_reg0x28_attr,

&aw9523b_reg0x29_attr,

&aw9523b_reg0x2a_attr,

&aw9523b_reg0x2b_attr,

&aw9523b_reg0x2c_attr,

&aw9523b_reg0x2d_attr,

&aw9523b_reg0x2e_attr,

&aw9523b_reg0x2f_attr,

};

 

static void aw9523b_enable_adv_conf(struct aw9523b_led *led)

{

int i, ret;

 

for (i = 0; i < ARRAY_SIZE(aw9523b_addr_attributes); i++) {

ret = device_create_file(&led->client->dev,

aw9523b_addr_attributes[i]);

if (ret) {

dev_err(&led->client->dev, "failed: sysfs file %s\n",

aw9523b_addr_attributes[i]->attr.name);

goto failed_remove_files;

}

}

 

return;

 

failed_remove_files:

for (i--; i >= 0; i--)

device_remove_file(&led->client->dev,

aw9523b_addr_attributes[i]);

}

 

static void aw9523b_disable_adv_conf(struct aw9523b_led *led)

{

int i;

 

for (i = 0; i < ARRAY_SIZE(aw9523b_addr_attributes); i++)

device_remove_file(&led->client->dev,

aw9523b_addr_attributes[i]);

}

 

static void aw9523b_led_work(struct work_struct *work)

{

struct aw9523b_led *led = container_of(work, struct aw9523b_led, work);

 

//printk("led%d: color = %d,brightness = %d\n",led->led_id, led->color, led->brightness);

aw9523b_set_brightness(led, led->led_id, led->color, led->brightness);

}

 

#define AW9523B_CONTROL_RGBS(name, id, clr) \

static void aw9523b_set_##name##_brightness(struct led_classdev *led_cdev,\

enum led_brightness value) \

{ \

struct aw9523b_led *led = \

container_of(led_cdev, struct aw9523b_led, cdev_##name); \

led->led_id = id; \

led->color = clr; \

led->brightness = value; \

schedule_work(&led->work); \

}

 

AW9523B_CONTROL_RGBS(led1r, LED1, LED1_RED);

AW9523B_CONTROL_RGBS(led1g, LED1, LED1_GREEN);

AW9523B_CONTROL_RGBS(led1b, LED1, LED1_BLUE);

 

AW9523B_CONTROL_RGBS(led2r, LED2, LED2_RED);

AW9523B_CONTROL_RGBS(led2g, LED2, LED2_GREEN);

AW9523B_CONTROL_RGBS(led2b, LED2, LED2_BLUE);

 

AW9523B_CONTROL_RGBS(led3r, LED3, LED3_RED);

AW9523B_CONTROL_RGBS(led3g, LED3, LED3_GREEN);

AW9523B_CONTROL_RGBS(led3b, LED3, LED3_BLUE);

 

static int aw9523b_register_led_classdev(struct aw9523b_led *led)

{

int ret;

 

INIT_WORK(&led->work, aw9523b_led_work);

 

//led1//

led->cdev_led1r.name = "led1_R";

led->cdev_led1r.brightness = LED_OFF;

led->cdev_led1r.brightness_set = aw9523b_set_led1r_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led1r.name);

goto failed_unregister_led1_R;

}

 

led->cdev_led1g.name = "led1_G";

led->cdev_led1g.brightness = LED_OFF;

led->cdev_led1g.brightness_set = aw9523b_set_led1g_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led1g.name);

goto failed_unregister_led1_G;

}

 

led->cdev_led1b.name = "led1_B";

led->cdev_led1b.brightness = LED_OFF;

led->cdev_led1b.brightness_set = aw9523b_set_led1b_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led1b.name);

goto failed_unregister_led1_B;

}

 

//led2//

led->cdev_led2r.name = "led2_R";

led->cdev_led2r.brightness = LED_OFF;

led->cdev_led2r.brightness_set = aw9523b_set_led2r_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led2r.name);

goto failed_unregister_led2_R;

}

 

led->cdev_led2g.name = "led2_G";

led->cdev_led2g.brightness = LED_OFF;

led->cdev_led2g.brightness_set = aw9523b_set_led2g_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led2g.name);

goto failed_unregister_led2_G;

}

 

led->cdev_led2b.name = "led2_B";

led->cdev_led2b.brightness = LED_OFF;

led->cdev_led2b.brightness_set = aw9523b_set_led2b_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led2b.name);

goto failed_unregister_led2_B;

}

 

//led3//

led->cdev_led3r.name = "led3_R";

led->cdev_led3r.brightness = LED_OFF;

led->cdev_led3r.brightness_set = aw9523b_set_led3r_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led3r);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led3r.name);

goto failed_unregister_led3_R;

}

 

led->cdev_led3g.name = "led3_G";

led->cdev_led3g.brightness = LED_OFF;

led->cdev_led3g.brightness_set = aw9523b_set_led3g_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led3g);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led3g.name);

goto failed_unregister_led3_G;

}

 

led->cdev_led3b.name = "led3_B";

led->cdev_led3b.brightness = LED_OFF;

led->cdev_led3b.brightness_set = aw9523b_set_led3b_brightness;

 

ret = led_classdev_register(&led->client->dev, &led->cdev_led3b);

if (ret < 0) {

dev_err(&led->client->dev, "couldn't register LED %s\n",

led->cdev_led3b.name);

goto failed_unregister_led3_B;

}

 

return 0;

 

failed_unregister_led3_B:

led_classdev_unregister(&led->cdev_led3g);

failed_unregister_led3_G:

led_classdev_unregister(&led->cdev_led3r);

failed_unregister_led3_R:

led_classdev_unregister(&led->cdev_led2b);

failed_unregister_led2_B:

led_classdev_unregister(&led->cdev_led2g);

failed_unregister_led2_G:

led_classdev_unregister(&led->cdev_led2r);

failed_unregister_led2_R:

led_classdev_unregister(&led->cdev_led1b);

failed_unregister_led1_B:

led_classdev_unregister(&led->cdev_led1g);

failed_unregister_led1_G:

led_classdev_unregister(&led->cdev_led1r);

failed_unregister_led1_R:

 

return ret;

}

 

static void aw9523b_unregister_led_classdev(struct aw9523b_led *led)

{

cancel_work_sync(&led->work);

led_classdev_unregister(&led->cdev_led1r);

led_classdev_unregister(&led->cdev_led1g);

led_classdev_unregister(&led->cdev_led1b);

led_classdev_unregister(&led->cdev_led2r);

led_classdev_unregister(&led->cdev_led2g);

led_classdev_unregister(&led->cdev_led2b);

led_classdev_unregister(&led->cdev_led3r);

led_classdev_unregister(&led->cdev_led3g);

led_classdev_unregister(&led->cdev_led3b);

}

 

int aw9523b_match_id(struct i2c_client *client)

{

u8 id = 0;

int tries = 5;

 

while(tries > 0)

{

aw9523b_read_byte(client,AW9523B_REG_CHIPID,&id);

printk("read led control chip id: 0x%x\n", id);

if (id == AW9523B_CHIPID_VALUE)

{

printk("matched");

return 0;

}

tries--;

}

return 1;

}

 

static int aw9523b_init(struct aw9523b_led *led)

{

int ret = 0;

struct i2c_client *client = led->client;

 

if (led->reset_gpio)

{

gpio_set_value(led->reset_gpio, 0);

 

/* Tacss = min 0.1ms */

udelay(20);

gpio_set_value(led->reset_gpio, 1);

udelay(100);

}

 

ret = aw9523b_match_id(client);

if(ret) {

dev_err(&led->client->dev, "Cannot read chip\n");

goto out;

}

 

aw9523b_write_byte(client,0x12,0x00);   //OUT4~9配置为呼吸灯模式

aw9523b_write_byte(client,0x12,0x00);   //OUT4~9配置为呼吸灯模式  

aw9523b_write_byte(client,0x13,0x00);   //OUT0~3配置为呼吸灯模式

aw9523b_write_byte(client,0x13,0x00);   //OUT0~3配置为呼吸灯模式

 

return 0;

out:

if (ret)

if (led->reset_gpio)

gpio_set_value(led->reset_gpio, 0);

return ret;

}

 

static int aw9523b_probe(struct i2c_client *client,

const struct i2c_device_id *id)

{

int ret;

struct aw9523b_led *led;

int gpio = 0;

 

printk("%s:enter\n",__FUNCTION__);

mutex_init(&aw9523b_mutex);

 

led = kzalloc(sizeof(struct aw9523b_led), GFP_KERNEL);

if (!led) {

dev_err(&client->dev, "failed to allocate driver data\n");

return -ENOMEM;

}

 

if (client->dev.of_node) {

/* Get GPIO from device tree */

gpio = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);

if (gpio < 0) {

dev_err(&client->dev,

"Failed to retrieve reset-gpios from device tree\n");

return gpio;

}

 

/* GPIO request and configuration */

ret = devm_gpio_request_one(&client->dev, gpio,

GPIOF_OUT_INIT_HIGH, "aw9523b_reset");

if (ret) {

dev_err(&client->dev, "Failed to request reset pin\n");

goto failed_free;

}

}

 

led->reset_gpio = gpio;

led->client = client;

 

init_rwsem(&led->rwsem);

 

i2c_set_clientdata(client, led);

 

ret = aw9523b_init(led);

if (ret)

goto failed_free;

 

ret = aw9523b_register_led_classdev(led);

if (ret) {

dev_err(&client->dev, "led register err: %d\n", ret);

goto failed_free;

}

 

//aw9523b_enable_adv_conf(led);

printk("%s:exit\n",__FUNCTION__);

return 0;

 

failed_free:

kfree(led);

 

return ret;

}

 

static int aw9523b_remove(struct i2c_client *client)

{

struct aw9523b_led *led = i2c_get_clientdata(client);

 

gpio_set_value(led->reset_gpio, 0);

aw9523b_unregister_led_classdev(led);

 

//aw9523b_disable_adv_conf(led);

 

kfree(led);

return 0;

}

 

static const struct i2c_device_id aw9523b_id[] = {

{ "aw9523b", 0 },

{ }

};

MODULE_DEVICE_TABLE(i2c, aw9523b_id);

 

#ifdef CONFIG_OF

static const struct of_device_id of_aw9523b_leds_match[] = {

{ .compatible = "xxx,aw9523b-leds", },

{},

};

MODULE_DEVICE_TABLE(of, of_aw9523b_leds_match);

#endif

 

static struct i2c_driver aw9523b_driver = {

.driver = {

.name = "aw9523b",

.of_match_table = of_match_ptr(of_aw9523b_leds_match),

},

.probe = aw9523b_probe,

.remove = aw9523b_remove,

.id_table = aw9523b_id,

};

module_i2c_driver(aw9523b_driver);

 

MODULE_DESCRIPTION("AW9523B LED driver");

MODULE_AUTHOR("Thornton Wu <447116230@qq.com>");

MODULE_LICENSE("GPL");

 

linux\drivers\leds\Makefile

obj-m += leds-aw9523b.o

2 KERNEL编译

export PATH=$PATH:/home/thornton/work/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/home/thornton/work/pi/firmware modules_install

 

3 KERNEL更新

将RPI T卡插在LINUX系统下,会弹出两个盘,一个是KERENL盘和一个BOOT盘

将firmware/lib拷到KERNEL盘,其他拷到BOOT盘

 

4 启动驱动

/boot/config.txt

dtoverlay=example-leds

 

/etc/ modules

leds-aw9523b

 

/etc/udev/rules.d/ 99-com.rules

SUBSYSTEM=="leds", PROGRAM="/bin/sh -c '\

        chmod 666 /sys/class/leds/led1_R/brightness;\

        chmod 666 /sys/class/leds/led1_G/brightness;\

        chmod 666 /sys/class/leds/led1_B/brightness;\

        chmod 666 /sys/class/leds/led2_R/brightness;\

        chmod 666 /sys/class/leds/led2_G/brightness;\

        chmod 666 /sys/class/leds/led2_B/brightness;\

        chmod 666 /sys/class/leds/led3_R/brightness;\

        chmod 666 /sys/class/leds/led3_G/brightness;\

        chmod 666 /sys/class/leds/led3_B/brightness;\

'"

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在嵌入式Linux下,设备树(device tree)用来描述硬件平台的各种资源,Linux内核在启动过程中,会解析设备树,获取各种硬件资源来初始化硬件。设备树的overlay功能是指可以在系统运行期间动态修改设备树。一般情况下,如上图所示,设备树经过DTC编译器编译为二进制的hello.dtb文件,加载到内存,随Linux内核一起启动后,一般就无法更改了。如果我们想修改设备树,需要修改hello.dts文件文件,重新编译成二进制文件:hello.dtb,然后重新启动内核,重新解析。有了设备树的overlay功能,省去了设备树的重新编译和内核重启,我们可以直接编写一个设备树插件:overlay.dts,编译成overlay.dtbo后,直接给设备树“打补丁”,在运行期间就可以动态添加节点、修改节点...设备树的overlay功能,在很多场合都会用得到,会让我们的开发更加方便:外界插拔设备,无法在设备树中预先描述:耳机树莓派 + FPGA开发板基于I2C的温度传感器管脚的重新配置:PIN multiplexing修改bootcmd、分区...设备树的overlay功能,目前还没有加入到内核mainline(linux-5.10.x),但目前有些开发板和配套的BSP已经支持了,支持在系统运行期间动态修改设备树文件。如果你手头的开发板或内核平台还没有支持device tree overlay,可以学习本期课程,学习内核中设备树overlay的实现原理,如何给内核打补丁,使内核支持设备树的overlay功能。有了本期课程的学习基础,明白了设备树overlay的实现原理和运行机制,你就可以尝试在自己的开发板平台上实现这个功能了。本期课程的主要内容如下:在开发板上如何实现设备树的overlay功能Configfs文件系统的配置与挂载Configfs编程接口如何编写设备树 overlay插件设备树 overlay的编译和运行设备树overlay运行机制分析本期课程适合哪些人学习:嵌入式驱动工程师嵌入式BSP工程师嵌入式软件工程师想从事嵌入式开发的同学全网首家讲解设备树overlay的视频教程。   

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值