LED入门

点亮一个LED灯一直是单片机或者嵌入式的一个入门程序了 不管是在哪来 只要一问 怎么入门嵌入式或者单片机 大家都会相看两不厌的回答:从点亮一个LED开始
在这里就主要的分析一下点亮一个led有多复杂

最基本的硬件操作:
1.选择GPIO引脚
2.配置GPIO引脚为输出模式
3.让GPIO输出0/1点亮led

stm32单片机上点亮一个led的操作:

#define LED_ON GPIO_SetBits(GPIOD, GPIO_Pin_13)        //端口置1
#define LED_OFF GPIO_ResetBits(GPIOD, GPIO_Pin_13)     //端口置0
void GPIO_Config(void)                            //GPIO初始配置
{
    GPIO_InitTypeDef GPIO_InitStructure;      //定义结构体变量

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); //使能GPIOD的时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;                    //指定引脚13
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //设置输出速率50MHz
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //推挽输出模式
    GPIO_Init(GPIOD, &GPIO_InitStructure);                            //初始化外设GPIOx寄存器
}

int main()
{
    GPIO_Config();            //GPIOD_1初始化配置
    while(1)
    {
        LED_ON;                    //点亮
        delay(1000);         //延时大概几百毫秒
        LED_OFF;                //熄灭
        delay(1000);
    }

}

那么有了os后呢(比如linux) 又是怎么点亮一盏led灯呢
linux下我们都是通过写一个Led模块的方法实现点亮led的操作

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include "LED.h"


#define LEDCON 0x56000010 
#define LEDDATA 0x56000014
unsigned int *led_config; 
unsigned int *led_data; 


struct cdev cdev;
 dev_t devno;


static int led_open (struct inode *inode, struct file *filp)
{
led_config=ioremap(LEDCON,4);
iowrite32(0x00015400,led_config);
led_data=ioremap(LEDDATA,4);

return 0;
}
static int led_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg)
{
switch(cmd){
case LED_ON :
iowrite32(0x00,led_data);
return 0;
case LED_OFF :
iowrite32(0xff,led_data);
return 0;
default :
return -EINVAL;}
}


 struct file_operations led_fops=
{
 .open = led_open,
 .ioctl = led_ioctl, 
};
static int led_init()
{
cdev_init(&cdev, &led_fops);
alloc_chrdev_region(&devno, 0, 1,"led");
cdev_add(&cdev, devno, 1);

return 0;
}
static int led_exit()
{ cdev_del(&cdev);
unregister_chrdev_region(devno,1);
}
module_init(led_init);
module_exit(led_exit);

对于linux下点亮一盏led灯 也是很简单 无非就是加了一些字符设备的封装 向用户空间注册一个/dev/led的节点供用户空间操作

如果是支持设备树的话就更简单的点亮一个led灯了
设备树上加入gpio管脚的配置

pinctrl-names = "default";
pinctrl-0 = <&gpio_pins>;

gpio_pins: gpio_pins {
                pinctrl-single,pins = <
                        AM33XX_IOPAD(0x83c, PIN_OUTPUT | MUX_MODE7) /* (U13) GPIO1[15] */
                >;
        };

测试gpio管脚为 gpio1_15
所以在User space对应的结点为1*32 + 15 = 47
1.选择对应的gpio口
echo 47 > /sys/class/gpio/export
2.设置对应gpio管口的输入输出方向 in or out
echo out > /sys/class/gpio/gpio47/direction
3.设置gpio输出的值 1为高电平 0为低电平
echo 1 > /sys/class/gpio/gpio47/value

那就加大一点点难度 在安卓平台上对led进行操作
在安卓平台上 也就是我们常用的手机设备 也是会有电源灯 背光灯 按键灯等诸多的灯光 这些灯要实现闪烁 rgb数值的操作
还有复杂的逻辑应用
首先明白一个手机对于一个灯的需求
1.可以设置led rgb值实现红绿蓝各种颜色的灯光 比如手机在充电时led会根据电量来显示不同颜色的灯光
2.可以闪烁 那这个就需要定时器的功能
3.各种灯的复杂操作 比如通知灯和电源灯是公用一个led 就会存在优先级
安卓系统主要的框架如下 所以对于led的操作也是在每层都有体现
APK ( Activity/Service ) —-第一层:Applications
|
|
|
XXXManager [ 这个Manager不是必须的 ] —-第二层:Framework
| [对aidl和service的封装而已]
AIDL ( *.aidl )
| [ IXXX.Stub.asInterface(ServiceManager.getService()) / Context.getSystemService() ]
service ( XXXService.java )
| [ native method ]
|
|
jni ( com_android_server_*.cpp ) —-第三层:Android Runtime/Dalvik
| [ hw_get_module(*_HARDWARE_MODULE_ID) ,使用dlopen/dlsym加载HAL]
|
|
HAL ( .default.so / ..so ) —-第四层:HAL
| [ 通过系统调用 open/read/write/ioctl来访问驱动,从而达到操作硬件的目的 ]
|
|
kernel driver ( *.so ) —-第五层:Linux Kernel

对于一个驱动工程师最拿手的当然是最底层的linux driver

以tiny4412平台为例子

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h> 
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

#include <linux/leds.h>

struct led_desc {
        int gpio;
        char *name;
};

static struct led_desc led_gpios[] = {
        {EXYNOS4212_GPM4(0), "led1"},
        {EXYNOS4212_GPM4(1), "led2"},
        {EXYNOS4212_GPM4(2), "led3"},
        {EXYNOS4212_GPM4(3), "led4"},
};

struct led_classdev_4412 {
        struct led_classdev cdev;
        int gpio;
};


static struct led_classdev_4412 *led_devs;
static void brightness_set_4412(struct led_classdev *led_cdev,
                          enum led_brightness brightness)
{
     struct led_classdev_4412 *dev = (struct led_classdev_4412 *)led_cdev;
        led_cdev->brightness = brightness;
        if (brightness != LED_OFF)
                gpio_set_value(dev->gpio, 0);
        else
                gpio_set_value(dev->gpio, 1);
}

static int leds_init(void)
{
        int i;
        int ret;

        /* 1. alloc led_classdev */
        led_devs = kzalloc(sizeof(struct led_classdev_4412) * sizeof(led_gpios)/sizeof(led_gpios[0]), GFP_KERNEL);
        if (led_devs == NULL) {
                printk("No memory for device\n");
                return -ENOMEM;
        }

        for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++)
        {
                s3c_gpio_cfgpin(led_gpios[i].gpio, S3C_GPIO_OUTPUT); 
                gpio_set_value(led_gpios[i].gpio, 1);

                /* 2. set */
                led_devs[i].cdev.max_brightness = LED_FULL;
                led_devs[i].cdev.brightness_set = brightness_set_4412;
                led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;
                led_devs[i].cdev.brightness = LED_OFF;
                led_devs[i].cdev.name = led_gpios[i].name;
                //led_devs[i].cdev.default_trigger = "timer";
                led_devs[i].gpio = led_gpios[i].gpio;

                /* 3. led_classdev_register */
                ret = led_classdev_register(NULL, &led_devs[i].cdev);
                if (ret) {
                        i--;
                        while (i >= 0) {
                                led_classdev_unregister(&led_devs[i].cdev);
                                i--;
                        }
                        kfree(led_devs);
                        return -EIO;
                }
        }

        return 0;
}

static void leds_exit(void)
{
        int i;
        for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++)
        {
                led_classdev_unregister(&led_devs[i].cdev);
        }
     kfree(led_devs);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EdisonGao");

这里会像用户空间提供接口文件 具体操作如下
操作如下
echo 255 > /sys/class/leds/led1/brightness
cat /sys/class/leds/led1/brightness
cat /sys/class/leds/led1/max_brightness

闪烁
echo timer > /sys/class/leds/led1/trigger
echo 100 > /sys/class/leds/led1/delay_on
echo 200 > /sys/class/leds/led1/delay_off

关闭
echo 0 > /sys/class/leds/led1/delay_on

echo 0 > /sys/class/leds/led1/brightness

实现了linux driver后就得实现hal层
这里以sony手机实现的灯光系统代码为例

#define LOG_TAG "lights.lt26"
#include <cutils/log.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <hardware/lights.h>
#include "lights.h"
/* Synchronization primities */
static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
/* Mini-led state machine */
static struct light_state_t g_notification;
static struct light_state_t g_battery;
static int g_backlight = 255;
/* The leds we have */

char const*const RED_LED_FILE           = "/sys/class/leds/red/brightness";
char const*const GREEN_LED_FILE         = "/sys/class/leds/green/brightness";
char const*const BLUE_LED_FILE          = "/sys/class/leds/blue/brightness";
char const*const RED_LED_FILE_TRIGGER     = "/sys/class/leds/red/trigger";
char const*const GREEN_LED_FILE_TRIGGER = "/sys/class/leds/green/trigger";
char const*const BLUE_LED_FILE_TRIGGER  = "/sys/class/leds/blue/trigger";
char const*const RED_LED_FILE_DELAYON   = "/sys/class/leds/red/delay_on";
char const*const GREEN_LED_FILE_DELAYON = "/sys/class/leds/green/delay_on";
char const*const BLUE_LED_FILE_DELAYON  = "/sys/class/leds/blue/delay_on";
char const*const RED_LED_FILE_DELAYOFF  = "/sys/class/leds/red/delay_off";
char const*const GREEN_LED_FILE_DELAYOFF    = "/sys/class/leds/green/delay_off";
char const*const BLUE_LED_FILE_DELAYOFF = "/sys/class/leds/blue/delay_off";
char const*const BUTTON_BACKLIGHT_FILE[] = {
  "/sys/class/leds/button-backlight/brightness",
  "/sys/class/leds/button-backlight-rgb1/brightness",
  "/sys/class/leds/button-backlight-rgb2/brightness",
  "/sys/class/leds/keyboard-backlight/brightness"
};
char const*const LCD_BACKLIGHT_FILE = "/sys/class/leds/lcd-backlight/brightness";
char const*const ALS_FILE   = "/sys/class/leds/lcd-backlight/als/enable";

enum {
    LED_RED,
    LED_GREEN,
    LED_BLUE,
    LED_BLANK
};
enum {
    MANUAL = 0,
    AUTOMATIC,
    MANUAL_SENSOR
};
static int write_int (const char *path, int value) {
    int fd;
    static int already_warned = 0;
    fd = open(path, O_RDWR);
    if (fd < 0) {
        if (already_warned == 0) {
            ALOGE("write_int failed to open %s\n", path);
            already_warned = 1;
        }
        return -errno;
    }
    char buffer[20];
    int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
    int written = write (fd, buffer, bytes);
    close(fd);
    return written == -1 ? -errno : 0;
}
static int write_string (const char *path, const char *value) {
    int fd;
    static int already_warned = 0;
    fd = open(path, O_RDWR);
    if (fd < 0) {
        if (already_warned == 0) {
            ALOGE("write_string failed to open %s\n", path);
            already_warned = 1;
        }
        return -errno;
    }
    char buffer[20];
    int bytes = snprintf(buffer, sizeof(buffer), "%s\n", value);
    int written = write (fd, buffer, bytes);
    close(fd);
    return written == -1 ? -errno : 0;
}
/* Color tools */
static int is_lit (struct light_state_t const* state) {
    return state->color & 0x00ffffff;
}
static int rgb_to_brightness (struct light_state_t const* state) {
    int color = state->color & 0x00ffffff;
    return ((77*((color>>16)&0x00ff))
            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
}
/* 设置LCD背光 */
static int set_light_backlight (struct light_device_t *dev, struct light_state_t const *state) {
    //light_state_t结构成员中color保存着rgb数据 这里通过rgb_to_brightness函数中的公式转换我们的rgb数据保存在brightness
    int brightness = rgb_to_brightness(state);
    //设置状态
    int als_mode;
    switch (state->brightnessMode) {
        case BRIGHTNESS_MODE_SENSOR:
            als_mode = AUTOMATIC;
            break;
        case BRIGHTNESS_MODE_USER:
            als_mode = BRIGHTNESS_MODE_USER;
            break;
        default:
            als_mode = MANUAL_SENSOR;
            break;
    }
    ALOGV("%s brightness=%d color=0x%08x", __func__,brightness,state->color);
    //设置lcd的brightness als_mode参数到/sys/class/leds/lcd-backlight/brightness文件下 就能控制lcd背光
    pthread_mutex_lock(&g_lock);
    g_backlight = brightness;
    write_int (ALS_FILE, als_mode);
    write_int (LCD_BACKLIGHT_FILE, brightness);
    pthread_mutex_unlock(&g_lock);
    return 0;
}
//安卓手机一般不会实现这个Buttons灯
static int set_light_buttons (struct light_device_t *dev, struct light_state_t const* state) {
    size_t i;
    int on = is_lit(state);
    pthread_mutex_lock(&g_lock);
    for (i = 0; i < sizeof(BUTTON_BACKLIGHT_FILE)/sizeof(BUTTON_BACKLIGHT_FILE[0]); i++) {
        write_int (BUTTON_BACKLIGHT_FILE[i],on?255:0);
    }
    pthread_mutex_unlock(&g_lock);
    return 0;
}

//这函数功能是通过写进/sys/class下相关的文件 控制led灯的颜色 闪烁时间 定时器
static void set_shared_light_locked (struct light_device_t *dev, struct light_state_t *state) {
    int r, g, b;
    int delayOn,delayOff;
    r = (state->color >> 16) & 0xFF;
    g = (state->color >> 8) & 0xFF;
    b = (state->color) & 0xFF;
        delayOn = state->flashOnMS;
    delayOff = state->flashOffMS;
    if (state->flashMode != LIGHT_FLASH_NONE) {
        write_string (RED_LED_FILE_TRIGGER, "timer");
        write_string (GREEN_LED_FILE_TRIGGER, "timer");
        write_string (BLUE_LED_FILE_TRIGGER, "timer");
        write_int (RED_LED_FILE_DELAYON, delayOn);
        write_int (GREEN_LED_FILE_DELAYON, delayOn);
        write_int (BLUE_LED_FILE_DELAYON, delayOn);
        write_int (RED_LED_FILE_DELAYOFF, delayOff);
        write_int (GREEN_LED_FILE_DELAYOFF, delayOff);
        write_int (BLUE_LED_FILE_DELAYOFF, delayOff);
    } else {
        write_string (RED_LED_FILE_TRIGGER, "none");
        write_string (GREEN_LED_FILE_TRIGGER, "none");
        write_string (BLUE_LED_FILE_TRIGGER, "none");
    }
    write_int (RED_LED_FILE, r);
    write_int (GREEN_LED_FILE, g);
    write_int (BLUE_LED_FILE, b);
}
static void handle_shared_battery_locked (struct light_device_t *dev) {
    //优先对通知灯处理 再对电池灯处理
    if (is_lit (&g_notification)) {
        set_shared_light_locked (dev, &g_notification);
    } else {
        set_shared_light_locked (dev, &g_battery);
    }
}

//电池灯和通知灯公用一个硬件灯 都是先保存state然后再调用handle_shared_battery_locked写进相关文件 
static int set_light_battery (struct light_device_t *dev, struct light_state_t const* state) {
    pthread_mutex_lock (&g_lock);
    g_battery = *state;
    handle_shared_battery_locked(dev);
    pthread_mutex_unlock (&g_lock);
    return 0;
}
static int set_light_notifications (struct light_device_t *dev, struct light_state_t const* state) {
    pthread_mutex_lock (&g_lock);
    g_notification = *state;
    handle_shared_battery_locked(dev);
    pthread_mutex_unlock (&g_lock);
    return 0;
}
/* Initializations */
void init_globals () {
    pthread_mutex_init (&g_lock, NULL);
}
/* Glueing boilerplate */
static int close_lights (struct light_device_t *dev) {
    if (dev)
        free(dev);
    return 0;
}
static int open_lights (const struct hw_module_t* module, char const* name,
                        struct hw_device_t** device) {
    int (*set_light)(struct light_device_t* dev,
                     struct light_state_t const *state);
    //根据传进来的参数设置set_light 比如电源灯 通知灯
    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
        set_light = set_light_backlight;
    }
    else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
        set_light = set_light_buttons;
    }
    else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
        set_light = set_light_battery;
    }
    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
        set_light = set_light_notifications;
    }
    else {
        return -EINVAL;
    }
    pthread_once (&g_init, init_globals);
    //分配light_device_t 这个结构体有两个成员 第一个成员肯定是一个hw_device_t结构体 紧接着是set_light的函数指针
    struct light_device_t *dev = malloc(sizeof (struct light_device_t));
    memset(dev, 0, sizeof(*dev));
    //设置light_device_t
    dev->common.tag     = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module  = (struct hw_module_t*)module;
    dev->common.close   = (int (*)(struct hw_device_t*))close_lights;
    dev->set_light      = set_light;
    *device = (struct hw_device_t*)dev;
    return 0;
}
static struct hw_module_methods_t lights_module_methods = {
    .open = open_lights,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = LIGHTS_HARDWARE_MODULE_ID,
    .name = "Sony lights module",
    .author = "Diogo Ferreira <defer@cyanogenmod.com>, Andreas Makris <Andreas.Makris@gmail.com>",
    .methods = &lights_module_methods,
};

hal层主要就是给jni文件提供一个库供其操作

继续是jni层的文件

#define LOG_TAG "LightsService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/lights.h>

#include <stdio.h>

namespace android
{

// These values must correspond with the LIGHT_ID constants in
// LightsService.java
enum {
    LIGHT_INDEX_BACKLIGHT = 0,
    LIGHT_INDEX_KEYBOARD = 1,
    LIGHT_INDEX_BUTTONS = 2,
    LIGHT_INDEX_BATTERY = 3,
    LIGHT_INDEX_NOTIFICATIONS = 4,
    LIGHT_INDEX_ATTENTION = 5,
    LIGHT_INDEX_BLUETOOTH = 6,
    LIGHT_INDEX_WIFI = 7,
    LIGHT_COUNT
};

struct Devices {
    light_device_t* lights[LIGHT_COUNT];
};

static light_device_t* get_device(hw_module_t* module, char const* name)
{
    int err;
    hw_device_t* device;
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (light_device_t*)device;
    } else {
        return NULL;
    }
}

static jlong init_native(JNIEnv *env, jobject clazz)
{
    int err;
    hw_module_t* module;
    Devices* devices;

    devices = (Devices*)malloc(sizeof(Devices));

    err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        devices->lights[LIGHT_INDEX_BACKLIGHT]
                = get_device(module, LIGHT_ID_BACKLIGHT);
        devices->lights[LIGHT_INDEX_KEYBOARD]
                = get_device(module, LIGHT_ID_KEYBOARD);
        devices->lights[LIGHT_INDEX_BUTTONS]
                = get_device(module, LIGHT_ID_BUTTONS);
        devices->lights[LIGHT_INDEX_BATTERY]
                = get_device(module, LIGHT_ID_BATTERY);
        devices->lights[LIGHT_INDEX_NOTIFICATIONS]
                = get_device(module, LIGHT_ID_NOTIFICATIONS);
        devices->lights[LIGHT_INDEX_ATTENTION]
                = get_device(module, LIGHT_ID_ATTENTION);
        devices->lights[LIGHT_INDEX_BLUETOOTH]
                = get_device(module, LIGHT_ID_BLUETOOTH);
        devices->lights[LIGHT_INDEX_WIFI]
                = get_device(module, LIGHT_ID_WIFI);
    } else {
        memset(devices, 0, sizeof(Devices));
    }

    return (jlong)devices;
}

static void finalize_native(JNIEnv *env, jobject clazz, jlong ptr)
{
    Devices* devices = (Devices*)ptr;
    if (devices == NULL) {
        return;
    }

    free(devices);
}

static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr,
        jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
    Devices* devices = (Devices*)ptr;
    light_state_t state;

    if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
        return ;
    }

    memset(&state, 0, sizeof(light_state_t));
    state.color = colorARGB;
    state.flashMode = flashMode;
    state.flashOnMS = onMS;
    state.flashOffMS = offMS;
    state.brightnessMode = brightnessMode;

    {
        ALOGD_IF_SLOW(50, "Excessive delay setting light");
        devices->lights[light]->set_light(devices->lights[light], &state);
    }
}

static JNINativeMethod method_table[] = {
    { "init_native", "()J", (void*)init_native },
    { "finalize_native", "(J)V", (void*)finalize_native },
    { "setLight_native", "(JIIIIII)V", (void*)setLight_native },
};

int register_android_server_LightsService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",
            method_table, NELEM(method_table));
}

};

JNI存在的作用就是向上注册本地方法 供用户空间java调用 向下加载hal文件 实现对硬件的操作
注册本地方法:jniRegisterNativeMethods
加载hal文件:hw_get_module->module->methods->open(module, name, &device);

register_android_server_LightsService函数在onload.cpp中进行注册

再上一层就是framework层对于lights这个服务的注册 和app层对这个服务的操作

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页