点亮一个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层对这个服务的操作