ARM40-A5應用——核心執行緒之於W1LED的應用
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
安裝核心模組詳情如下:
[email protected]:~# insmod w1led.ko
create ktrhead ok.
[email protected]:~# rmmod w1led.ko
w1led free
Leaving w1_process.
kw1ledd stopped.
[email protected]:~# insmod w1led.ko
create ktrhead ok.
[email protected]:~# 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;
}