ARM40-A5应用——内核线程之于W1LED的应用
2018.11.01
版权声明:本文为博主原创文章,允许转载。
ARM40的一些产品上需要用到较多的LED指示灯,此时推荐使用W1LED的方法来实现。
W1LED监测GPIO、串口(RS232和RS485)、CAN等外设的输入/输出状态,定期(例如每200ms)给LED指示灯板发送数据,控制LED指示灯的亮/灭。
LED灯板上有一颗MCU,接收W1LED发来的数据,根据接收到的指令,来控制灯板上的LED。
W1LED使用3根线(VCC、GND、1-wire)即可,并且多个灯板可共享主板上的3根线,根据各自的ID来执行发给自己的指令。
W1LED方法占用IO少,使用灵活方便,并且LED反应了主机的工作状态,而不仅仅是反应了局部硬件电路的状态。当然,也可以在电路板上增加硬件控制的指示设备,与W1LED的方法并无冲突。
一、内核线程
1.1、内核线程源码
W1LED需定期(例如200ms)给LED灯板发数据。内核线程启动后按周期性间隔运行,能够实现这个任务。代码见附(1),共包含3个文件:
w1led_kthread.c
w1led_io.c
w1led_io.h
1.2、Makefile
MODULE_NAME := w1led
obj-m += $(MODULE_NAME).o
$(MODULE_NAME)-objs := w1led_kthread.o w1led_io.o
PWD := $(shell pwd)
KDIR := /home/arm40_a5d3_kernel/linux-at91-master
CC := /home/arm-2014.05/bin/arm-none-linux-gnueabi-gcc
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.symvers *.order
make后生成 w1led.ko文件。
注意:
拷贝Makefile后,需要将命令前面的空格修正为"TAB空格“。对Makefile文件来说,"TAB空格"用于指示本行为命令。
1.3、安装内核模块
将w1led.ko文件拷贝到ARM40:
rmmod w1led.ko
lsmod
insmod w1led.ko
安装内核模块详情如下:
root@ARM40:~# insmod w1led.ko
create ktrhead ok.
root@ARM40:~# rmmod w1led.ko
w1led free
Leaving w1_process.
kw1ledd stopped.
root@ARM40:~# insmod w1led.ko
create ktrhead ok.
root@ARM40:~# lsmod
Module Size Used by Tainted: G
w1led 1515 0
二、运行情况
安装内核模块后,内核线程立即就会运行,top -b命令摘录如下:
可以看到kw1ledd的运行情况。
另外可以看到LED灯板上的LED在闪烁了。
参考文章
Linux内核drivers/w1/w1_io.c
linux模块编程(二)——运行不息的内核线程kthread
https://blog.csdn.net/qb_2008/article/details/6835783
Linux内核多线程(二)
https://www.cnblogs.com/zhuyp1015/archive/2012/06/11/2545702.html
ARM40-A5应用程序——温度传感器DS18B20的驱动与应用
https://blog.csdn.net/vonchn/article/details/83586870
附:
(1)w1led线程源码
(1a) w1led_kthread.c
#include "w1led_io.h"
#include <linux/sched.h>
#include <linux/kthread.h>
static struct task_struct * _tsk;
int w1_process(void* data)
{
unsigned char buf[2];
const unsigned long jtime = msecs_to_jiffies(100); //100ms
do {
w1_reset_bus();
w1_write_8(0xcc);
w1_write_8(0x44);
w1_reset_bus();
w1_write_8(0xcc);
w1_write_8(0xbe);
buf[0] = w1_read_8();
buf[1] = w1_read_8();
DPRINTK("b0=%x,b1=%x\n",buf[0],buf[1]);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(jtime);
} while(!kthread_should_stop());
DPRINTK("Leaving w1_process.\n");
return 0;
}
static int __init w1led_init(void)
{
int ret;
ret = gpio_request(DQ, "W1LED");
if (ret) {
DPRINTK("unable to request GPIO for W1LED.\n");
return ret;
}
gpio_direction_output(DQ,1); //初始时DQ为1
udelay(1);
_tsk = kthread_run(w1_process, NULL, "kw1ledd");
if (IS_ERR(_tsk)) { //判断线程是否有效
DPRINTK("create kthread fail.\n");
return -1;
} else
DPRINTK(KERN_INFO "create ktrhead ok.\n");
return 0;
}
static void __exit w1led_exit(void)
{
DPRINTK("w1led free\n");
if (!IS_ERR(_tsk)){
kthread_stop(_tsk);
printk(KERN_INFO "kw1ledd stopped.\n");
}
gpio_free(DQ);
}
module_init(w1led_init);
module_exit(w1led_exit);
MODULE_AUTHOR("rit <[email protected]>");
MODULE_DESCRIPTION("ds18b20 driver");
MODULE_LICENSE("GPL");
(1b) w1led_io.h
#include <linux/delay.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#define DEBUG
#if defined(DEBUG)
#define DPRINTK(fmt,arg...) printk(fmt,##arg);
#else
#define DPRINTK(fmt,arg...)
#endif
#define DQ 57 //148=PE20(32*4+20),57=PB25(32*1 + 25)
int w1_reset_bus(void);
void w1_write_8(unsigned char byte);
unsigned char w1_read_8(void);
(1c) w1led_io.c
#include "w1led_io.h"
int w1_reset_bus(void)
{
//向ds18b20发送一个500us低电平复位信号
gpio_direction_output(DQ,0); //Drives DQ low
udelay(500);
gpio_direction_input(DQ); //Releases the bus
udelay(70);
//检测到DQ上为高, 复位失败; 为低电平,复位成功
if(gpio_get_value(DQ)) // Sample for presence pulse from slave
return -1;
//等待复位时隙完毕后,继续将DQ置为高电平
udelay(410); // Complete the reset sequence recovery
gpio_direction_output(DQ,1);
return 0;
}
static inline void w1_write_bit(int bit)
{
if (bit) { // Write '1' bit
gpio_direction_output(DQ,0); // Drives DQ low
udelay(6);
gpio_direction_input(DQ); // Releases the bus
udelay(64);
} else { // Write '0' bit
gpio_direction_output(DQ,0); // Drives DQ low
udelay(60);
gpio_direction_input(DQ); // Releases the bus
udelay(10);
}
}
void w1_write_8(unsigned char byte)
{
int i;
for (i = 0; i < 8; i++) {
w1_write_bit((byte >> i) & 0x1);
}
}
static inline unsigned char w1_read_bit(void)
{
int result;
//sample timing is critical here
gpio_direction_output(DQ,0); //Drives DQ low
udelay(6);
gpio_direction_input(DQ); //Releases the bus
udelay(2); //udelay(9); for some situation
result = gpio_get_value(DQ) ? 1 : 0; //Sample the bit value from the slave
udelay(55); //Complete the time slot and 10us recovery
return result & 0x1;
}
unsigned char w1_read_8(void)
{
int i;
unsigned char result = 0;
for (i = 0; i < 8; ++i)
result |= (w1_read_bit() << i);
return result;
}