我们希望在使用嵌入式系统的时候越简单方便越好,我要讲的开机自动启动程序是简便化的第一步,下面将我的操作步骤记录如下:
要设置自启动程序需要解决两个问题:1、系统自动登录;2、自动执行脚本,
1)系统自动登录
(1)修改/etc/inittab
在inittab中,有如下配置
1:2345:respawn:/sbin/agetty tty1 9600
2:2345:respawn:/sbin/agetty tty2 9600
3:2345:respawn:/sbin/agetty tty3 9600
4:2345:respawn:/sbin/agetty tty4 9600
5:2345:respawn:/sbin/agetty tty5 9600
6:2345:respawn:/sbin/agetty tty6 9600
表示系统可以有六个控制台,可以用ALT+(F1~F6)来切换。而/sbin/agetty就是一个登陆验证程序,执行它,会提示用户输入用户名和密码,然后启动一个指定的shell(在passwd文件中指定的)。
所以,我们只需将其修改为不执行agettty,而是执行自己编写的一个脚本,就可以跳过用户名和密码的输入。修改如下:
1:2345:respawn:/root/logintest tty1 9600
这里我在/root/下放了一个测试脚本,这样系统启动时就会执行那个脚本,脚本内容:
#!/bin/sh
/bin/login -f root
脚本其实就是执行了一个root用户的登陆而已。
2)自动执行程序
方法有两种,一种是将要执行的代码放到1)中的/root/logintest的后面;
另一种是将要执行的代码放到/etc/profile文件的后面即可。
添加平台支持,其实本质是利用各平台SDK环境创建原生工程文件。所以,所有函数也是围绕这一主题展开。
入口当然是platform函数
module.exports = function platform(command, targets, callback) {
.....
};
先不看具体源码,再看其他几个函数
(1)module.exports.supports = function(project_root, name, callback) {
这个函数主要执行以下几步:
//检查传入参数
//判断平台是否支持
//获得顶层目录下platforms.js中parser参数指定的各平台解析文件
//检查各平台依赖的SDK是否存在
/** * Check Platform Support. * * Options: * * - {String} `name` of the platform to test. * - {Function} `callback` is triggered with the answer. * - {Error} `e` null when a platform is supported otherwise describes error. */ module.exports.supports = function(project_root, name, callback) { // required parameters if (!name) throw new Error('requires a platform name parameter'); if (!callback) throw new Error('requires a callback parameter'); // check if platform exists var platform = platforms[name]; if (!platform) { callback(new Error(util.format('"%s" platform does not exist', name))); return; } // look up platform meta-data parser var platformParser = platforms[name].parser; if (!platformParser) { callback(new Error(util.format('"%s" platform parser does not exist', name))); return; } // check for platform support platformParser.check_requirements(project_root, function(e) { // typecast String to Error e = (typeof e == 'string') ? new Error(e) : e; // typecast false Boolean to null e = (e) ? e : null; callback(e); }); };
(2)function call_into_create(target, projectRoot, cfg, id, version, callback, end) {
这个函数调用各个移动开发平台SDK环境中提供的创建原生工程文件的方法,创建工程
function call_into_create(target, projectRoot, cfg, id, version, callback, end) { var output = path.join(projectRoot, 'platforms', target); // Check if output directory already exists. if (fs.existsSync(output)) { var err = new Error('Platform "' + target + '" already exists at "' + output + '"'); if (callback) callback(err); else throw err; } else { // Make sure we have minimum requirements to work with specified platform events.emit('log', 'Checking if platform "' + target + '" passes minimum requirements...'); module.exports.supports(projectRoot, target, function(err) { if (err) { if (callback) callback(err); else throw err; } else { // Create a platform app using the ./bin/create scripts that exist in each repo. // Run platform's create script var bin = path.join(cordova_util.libDirectory, target, id, version, 'bin', 'create'); //调用不同平台下创建工程命令 if(target == 'wp7') bin = path.join(cordova_util.libDirectory, 'wp', id, version, 'wp7', 'bin', 'create'); if(target == 'wp8') bin = path.join(cordova_util.libDirectory, 'wp', id, version, 'wp8', 'bin', 'create'); var args = (target=='ios') ? '--arc' : ''; var pkg = cfg.packageName().replace(/[^\w.]/g,'_'); var name = cfg.name(); var command = util.format('"%s" %s "%s" "%s" "%s"', bin, args, output, pkg, name); events.emit('log', 'Running bin/create for platform "' + target + '" with command: "' + command + '" (output to follow)'); shell.exec(command, {silent:true,async:true}, function(code, create_output) { events.emit('log', create_output); if (code > 0) { var err = new Error('An error occured during creation of ' + target + ' sub-project. ' + create_output); if (callback) callback(err); else throw err; } else { require('../cordova').prepare(target, function(err) { if (err) { if (callback) callback(err); else throw err; } else { createOverrides(projectRoot, target); end(); //platform add is done by now. // Install all currently installed plugins into this new platform. var plugins_dir = path.join(projectRoot, 'plugins'); var plugins = cordova_util.findPlugins(plugins_dir); var parser = new platforms[target].parser(output); plugins && plugins.forEach(function(plugin) { events.emit('log', 'Installing plugin "' + plugin + '" following successful platform add of ' + target); plugman.install(target, output, path.basename(plugin), plugins_dir, { www_dir: parser.staging_dir() }); }); } }); } }); } }); } }
(3)最会回到platform函数
module.exports = function platform(command, targets, callback) { var projectRoot = cordova_util.isCordova(process.cwd()); //检查是否在根目录下存在.cordova目录,如果存在此目录才是cordova项目,返回此绝对路径;否则返回false if (!projectRoot) { var err = new Error('Current working directory is not a Cordova-based project.'); if (callback) callback(err); else throw err; return; } var hooks = new hooker(projectRoot); if (arguments.length === 0) command = 'ls';//参数为空,默认为ls if (targets) { if (!(targets instanceof Array)) targets = [targets];//遍历targets对象是否是系统支持平台参数 targets.forEach(function(t) { if (!(t in platforms)) { var err = new Error('Platform "' + t + '" not recognized as core cordova platform.'); if (callback) return callback(err); else throw err; } }); } else { if (command == 'add' || command == 'rm') { var err = new Error('You need to qualify `add` or `remove` with one or more platforms!'); if (callback) return callback(err); else throw err; } } var xml = cordova_util.projectConfig(projectRoot);//根目录/www/config.xml var cfg = new cordova_util.config_parser(xml);//解析xml文件 var opts = { platforms:targets }; switch(command) { case 'add': var end = n(targets.length, function() { hooks.fire('after_platform_add', opts, function(err) { if (err) { if (callback) callback(err); else throw err; } else { if (callback) callback(); } }); }); hooks.fire('before_platform_add', opts, function(err) { if (err) { if (callback) callback(err); else throw err; } else { var config_json = config.read(projectRoot); targets.forEach(function(t) { lazy_load.based_on_config(projectRoot, t, function(err) { if (err) { if (callback) callback(err); else throw err; } else { if (config_json.lib && config_json.lib[t]) { call_into_create(t, projectRoot, cfg, config_json.lib[t].id, config_json.lib[t].version, callback, end); } else { call_into_create(t, projectRoot, cfg, 'cordova', platforms[t].version, callback, end); } } }); }); } }); break; case 'rm': case 'remove': var end = n(targets.length, function() { hooks.fire('after_platform_rm', opts, function(err) { if (err) { if (callback) callback(err); else throw err; } else { if (callback) callback(); } }); }); hooks.fire('before_platform_rm', opts, function(err) { if (err) { if (callback) callback(err); else throw err; } else { targets.forEach(function(target) { shell.rm('-rf', path.join(projectRoot, 'platforms', target)); shell.rm('-rf', path.join(cordova_util.appDir(projectRoot), 'merges', target)); var plugins_json = path.join(projectRoot, 'plugins', target + '.json'); if (fs.existsSync(plugins_json)) shell.rm(plugins_json); end(); }); } }); break; case 'ls': case 'list': default: var platforms_on_fs = cordova_util.listPlatforms(projectRoot); hooks.fire('before_platform_ls', function(err) { if (err) { if (callback) callback(err); else throw err; } else { events.emit('results', (platforms_on_fs.length ? platforms_on_fs : 'No platforms added. Use `cordova platform add <platform>`.')); hooks.fire('after_platform_ls', function(err) { if (err) { if (callback) callback(err); else throw err; } }); } }); break; } };
结构比较简单,参数检查,然后根据不同参数执行不同动作,需要唯一说明的是,在switch函数中出现的
var end = n(targets.length, function() { hooks.fire('after_platform_add', opts, function(err) { if (err) { if (callback) callback(err); else throw err; } else { if (callback) callback(); } }); });
这个函数是使用的node的nCallbacks插件,https://npmjs.org/package/ncallbacks
官方文档解释是function that executes n times,在上面这段代码中意思就是,end函数只能被执行targets.length次,调用超过targets.length次再调用end,其内部函数也不会再被执行,其实就是限制次数,避免当平台列表中为空后还误执行
A20GPIO中断类型差别结果迥异的问题思考
最近在使用全志A20做开发时,发现在处理中断的时候,用电平触发模式,报中断比较乱,用边沿触发则很稳定,不会乱报。笔者感到比较困惑,笔者用电平触发写的code如下:
reverseHandle = sw_gpio_irq_request(gReverseCar_gpio_hdle.gpio, TRIG_LEVL_HIGH, \ &sw_reverseCar_irq_Handle, &privateData); if (!reverseHandle) { printk("Failed to get gpio irq for reverse car detection\n"); } privateData.trigtype = TRIG_LEVL_HIGH; static int sw_reverseCar_irq_Handle(void *para){ printk("IRQ sw_reverseCar_irq: %d ", privateData.trigtype); printk("IO:%d, value:%d\n",gReverseCar_gpio_hdle.gpio, \ __gpio_get_value(gReverseCar_gpio_hdle.gpio)); sw_gpio_eint_set_enable(gReverseCar_gpio_hdle.gpio, 0); sw_gpio_eint_clr_irqpd_sta(gReverseCar_gpio_hdle.gpio); //TRIG_LEVL_LOW TRIG_LEVL_HIGH TRIG_EDGE_POSITIVE TRIG_EDGE_NEGATIVE if(privateData.trigtype == TRIG_LEVL_HIGH) { if(0 == sw_gpio_eint_set_trigtype(gReverseCar_gpio_hdle.gpio,TRIG_LEVL_LOW)) privateData.trigtype = TRIG_LEVL_LOW; } else if(privateData.trigtype == TRIG_LEVL_LOW) { if(0 == sw_gpio_eint_set_trigtype(gReverseCar_gpio_hdle.gpio,TRIG_LEVL_HIGH)) privateData.trigtype = TRIG_LEVL_HIGH; } sw_gpio_eint_set_enable(gReverseCar_gpio_hdle.gpio, 1); return 0; }
中断类型设置成DEBO中断打印信息有问题,具体如下:
root@android:/ # [ 519.018955] IRQ sw_reverseCar_irq: 2 IO:188, value:1 [ 519.024773] IRQ sw_reverseCar_irq: 3 IO:188, value:1 [ 519.030576] IRQ sw_reverseCar_irq: 2 IO:188, value:1 [ 519.036378] IRQ sw_reverseCar_irq: 3 IO:188, value:1 [ 519.042169] IRQ sw_reverseCar_irq: 2 IO:188, value:1 [ 520.898643] IRQ sw_reverseCar_irq: 3 IO:188, value:0 [ 520.904456] IRQ sw_reverseCar_irq: 2 IO:188, value:0 [ 520.910363] IRQ sw_reverseCar_irq: 3 IO:188, value:0 [ 520.916215] IRQ sw_reverseCar_irq: 2 IO:188, value:0 [ 520.921995] IRQ sw_reverseCar_irq: 3 IO:188, value:0 [ 520.927832] IRQ sw_reverseCar_irq: 2 IO:188, value:0 [ 520.933612] IRQ sw_reverseCar_irq: 3 IO:188, value:0 [ 520.939432] IRQ sw_reverseCar_irq: 2 IO:188, value:0 [ 520.945317] IRQ sw_reverseCar_irq: 3 IO:188, value:0
用边沿触发写的code如下,
gPrivData.trigtype = TRIG_EDGE_POSITIVE; gPrivData.irqReqHandle = sw_gpio_irq_request(gPrivData.gpio_hdle.gpio, TRIG_EDGE_POSITIVE, \ (peint_handle)&sw_reverseCar_irq_Handle, (void *)&gPrivData); static int sw_reverseCar_irq_Handle(void *para){ //printk("IRQ sw_reverseCar_irq: %d ", gPrivData.trigtype); gPrivData.CarStatus = __gpio_get_value(gPrivData.gpio_hdle.gpio); //printk("io:%d, value:%d\n",gPrivData.gpio_hdle.gpio, gPrivData.CarStatus); /*if app has no capacity for handle reverse event, system need reboot*/ if(1 == gPrivData.needRebootFlag) { printk("Reboot for handle reverse in boot now!\n"); *(volatile __u32 *)(0xf1c20c94) = 0x3; } up(&gPrivData.sem_lock); sw_gpio_eint_set_enable(gPrivData.gpio_hdle.gpio, 0); sw_gpio_eint_clr_irqpd_sta(gPrivData.gpio_hdle.gpio); if(gPrivData.trigtype == TRIG_EDGE_POSITIVE) { if(0 == sw_gpio_eint_set_trigtype(gPrivData.gpio_hdle.gpio,TRIG_EDGE_NEGATIVE)) gPrivData.trigtype = TRIG_EDGE_NEGATIVE; } else if(gPrivData.trigtype == TRIG_EDGE_NEGATIVE) { if(0 == sw_gpio_eint_set_trigtype(gPrivData.gpio_hdle.gpio,TRIG_EDGE_POSITIVE)) gPrivData.trigtype = TRIG_EDGE_POSITIVE; } sw_gpio_eint_set_enable(gPrivData.gpio_hdle.gpio, 1); return 0; }
EDGE时还可以设置中断类型为TRIG_EDGE_DOUBLE,笔者暂时没有采用,中断打印信息正常,如下:
root@android:/ # [ 517.116361] IRQ sw_reverseCar_irq: 0 io:188, value:1 [ 518.103095] IRQ sw_reverseCar_irq: 1 io:188, value:0 root@android:/ # [ 520.918512] IRQ sw_reverseCar_irq: 0 io:188, value:1 [ 521.876038] IRQ sw_reverseCar_irq: 1 io:188, value:0 [ 527.796107] IRQ sw_reverseCar_irq: 0 io:188, value:1 [ 528.843397] IRQ sw_reverseCar_irq: 1 io:188, value:0 [ 531.335858] IRQ sw_reverseCar_irq: 0 io:188, value:1 [ 532.306660] IRQ sw_reverseCar_irq: 1 io:188, value:0 [ 534.552011] IRQ sw_reverseCar_irq: 0 io:188, value:1 [ 535.101586] IRQ sw_reverseCar_irq: 1 io:188, value:0
笔者不清楚原因,怀疑是芯片驱动的问题,但是驱动也就那样写了啊,难道是芯片设计的问题,当然这种也就是在刚有中断时会有乱irq上来,如果用delaywork延时一下处理也是可以绕过去的。望哪位大侠清楚的解释一下。