一、电路图
3个165串联组成扩展24路光耦输入检测,一个595扩展8路继电器输出
二、驱动源码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>//jiffies在此头文件中定义
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <linux/delay.h>
static struct hrtimer kthread_timer;
#define DISP_BUF_LEN 2
#define DISP_DIG_LEN 4
typedef struct _disp_buf_t{
uint8_t output[DISP_BUF_LEN];
uint8_t dig[DISP_DIG_LEN]
}disp_buf_t;
disp_buf_t disp_buf;;
#define SERIAL_CLK_PIN NUC980_PB0
#define SERIAL_PE_PIN NUC980_PB3
#define SERIAL_LE_PIN NUC980_PB2
#define SERIAL_IN_PIN NUC980_PB6
#define SERIAL_OUT_PIN NUC980_PB4
#define serial_clk_pin(sta) gpio_set_value(SERIAL_CLK_PIN, sta)
#define serial_pe_pin(sta) gpio_set_value(SERIAL_PE_PIN, sta)
#define serial_le_pin(sta) gpio_set_value(SERIAL_LE_PIN, sta)
#define serial_in_pin() gpio_get_value(SERIAL_IN_PIN)
#define serial_out_pin(sta) gpio_set_value(SERIAL_OUT_PIN, sta)
#define LED_PIN NUC980_PB13
#define EXTIO_IN_MAX 24
#define EXTIO_OUT_MAX 8
#define EXTIO_KEY_MAX 2
#define EXTIO_CHECK_AMX 2
#define EXTIO_SET_KEY 0
#define EXTIO_OPEN_KEY 1
#define EXTIO_KEY_RELEASE 0
#define EXTIO_KEY_PRESS 1
#define EXTIO_KEY_KEEP 2
#define EXTIO_CHECK_POWERDOWN 0
#define EXTIO_CHECK_EMERGENCY 1
typedef struct _input_t{
uint8_t buf[EXTIO_IN_MAX/8];
uint8_t save[EXTIO_IN_MAX/8];
uint8_t timer[EXTIO_IN_MAX];
}input_t;
typedef struct _output_t{
uint8_t byte[EXTIO_OUT_MAX/8];
}output_t;
typedef struct _check_t{
uint8_t sta[EXTIO_CHECK_AMX];
uint8_t time[EXTIO_CHECK_AMX];
}check_t;
typedef struct _extio_t{
input_t input;
output_t output;
key_t key;
check_t check;
}extio_t;
extio_t extio;
void extio_write(uint8_t byte)
{
uint8_t i;
for( i=0; i<8; i++ ){
if( byte&(0x80) )
serial_out_pin(1);
else
serial_out_pin(0);
serial_clk_pin(1);
udelay(100);
serial_clk_pin(0);
udelay(100);
byte<<=1;
}
}
void extio_output(void)
{
uint8_t i;
for( i=0; i<EXTIO_OUT_MAX/8; i++ ){
extio_write(extio.output.byte[i]);
// extio_write(0x55);
// printk("modlog: int %s, line %d.\n", __FUNCTION__, __LINE__);
}
serial_le_pin(1);
udelay(100);
serial_le_pin(0);
}
void extio_read(uint8_t *byte,uint8_t first)
{
*byte = 0;
uint8_t i;
if( first ){
if( serial_in_pin() )
(*byte) |= 0x01;
for( i=0; i<7; i++ ){
serial_clk_pin(1);
(*byte)<<=1;
if( serial_in_pin() )
(*byte) |= 0x01;
serial_clk_pin(0);
}
}
else{
for( i=0; i<8; i++ ){
serial_clk_pin(1);
(*byte)<<=1;
if( serial_in_pin() )
(*byte) |= 0x01;
serial_clk_pin(0);
}
}
}
void extio_input(void)
{
serial_pe_pin(0);
udelay(1);
serial_pe_pin(1);
// rt_hw_us_delay(10);
uint8_t *in_ptr =&extio.input.buf[EXTIO_IN_MAX/8-1];
uint8_t i;
for( i=0; i<EXTIO_IN_MAX/8; i++ ){
//
if( i==0 )
extio_read(in_ptr,1);
else
extio_read(in_ptr,0);
in_ptr--;
}
// printk("%02x %02x %02x\n",extio.input.buf[0],extio.input.buf[1],extio.input.buf[2]);
}
void extio_input_check( void )
{
uint8_t i,io,k,num;
for( num=0; num<EXTIO_IN_MAX/8; num++ )
{
if( extio.input.buf[num]!=extio.input.save[num] )
{
io = extio.input.buf[num]^extio.input.save[num];
for( i=0; i<8; i++ )
{
if( io&0x01 )
{
if( extio.input.timer[num*8+i]<30 )
{
extio.input.timer[num*8+i]++; //ÿһ¸öIO¿ڶԓ¦һ¸ö¼Ɗ±Ʒ
}
else
{
extio.input.timer[num*8+i] = 0;
k = extio.input.buf[num];
k >>= i;
if( k&0x01 )
{
extio.input.save[num] |= (1<<i);
}
else
{
extio.input.save[num] &= (~(1<<i));
}
}
}
else
{
extio.input.timer[num*8+i] = 0;
}
io >>= 1;
}
}
}
}
void extio_task(void)
{
// extio_input();
extio_output();
}
#define HRTIMER_TEST_CYCLE 0, (100000000 / 2)
enum hrtimer_restart hrtimer_cb_func(struct hrtimer *timer)
{
extio_task();
hrtimer_forward(timer, timer->base->get_time(), ktime_set(HRTIMER_TEST_CYCLE));
return HRTIMER_RESTART;
}
void kthread_hrtimer_init(void) {
hrtimer_init(&kthread_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
kthread_timer.function = hrtimer_cb_func;
hrtimer_start(&kthread_timer, ktime_set(HRTIMER_TEST_CYCLE), HRTIMER_MODE_REL);
printk("timer init\n");
}
static int major = 0;
static const char *extio_dev_name = "extio";
static struct class *extio_dev_class;
static struct device *extio_dev_device;
static char dev_buf[4096];
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int extio_dev_open(struct inode *node, struct file *file)
{
return 0;
}
static int extio_dev_close(struct inode *node, struct file *file)
{
return 0;
}
static ssize_t extio_dev_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
int ret;
ret = copy_to_user(buf, dev_buf, MIN(size, 4096)); // 从内核空间拷贝数据到用户空间
return ret;
}
static ssize_t extio_dev_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int ret;
ret = copy_from_user(dev_buf, buf, MIN(size, 4096)); // 从用户空间拷贝数据到内核空间
extio.output.byte[0] = simple_strtol(dev_buf,&dev_buf,3);
printk("write %02x\n",extio.output.byte[0]);
return ret;
}
static const struct file_operations extio_dev_fops = {
.owner = THIS_MODULE,
.open = extio_dev_open,
.release = extio_dev_close,
.read = extio_dev_read,
.write = extio_dev_write,
};
static int __init extio_dev_init(void)
{
int ret;
ret = gpio_request(NUC980_PB0,"NUC980_PB0");
if(ret < 0){
printk(KERN_EMERG "gpio_request NUC980_PB0 failed!\n");
}
ret = gpio_request(NUC980_PB3,"NUC980_PB3");
if(ret < 0){
printk(KERN_EMERG "gpio_request NUC980_PB3 failed!\n");
}
ret = gpio_request(NUC980_PB2,"NUC980_PB2");
if(ret < 0){
printk(KERN_EMERG "gpio_request NUC980_PB2 failed!\n");
}
ret = gpio_request(NUC980_PB4,"NUC980_PB4");
if(ret < 0){
printk(KERN_EMERG "gpio_request NUC980_PB4 failed!\n");
}
ret = gpio_request(NUC980_PB6,"NUC980_PB6");
if(ret < 0){
printk(KERN_EMERG "gpio_request NUC980_PB6 failed!\n");
}
gpio_direction_output(SERIAL_CLK_PIN,1);
gpio_direction_output(SERIAL_LE_PIN,1);
gpio_direction_output(SERIAL_PE_PIN,1);
gpio_direction_input(SERIAL_IN_PIN);
gpio_direction_output(SERIAL_OUT_PIN,1);
gpio_direction_output(LED_PIN,1);
gpio_set_value(LED_PIN, 0);
// timer_init();
kthread_hrtimer_init();
printk("modlog: func %s, line %d.\n", __FUNCTION__, __LINE__);
major = register_chrdev(0, extio_dev_name, &extio_dev_fops); // 注册字符设备,第一个参数0表示让内核自动分配主设备号
extio_dev_class = class_create(THIS_MODULE, "extio_dev_class"); //
if (IS_ERR(extio_dev_class))
{
unregister_chrdev(major, extio_dev_name);
return -1;
}
extio_dev_device = device_create(extio_dev_class, NULL, MKDEV(major, 0), NULL, extio_dev_name); // 创建设备节点创建设备节点,成功后就会出现/dev/char_dev_name的设备文件
if (IS_ERR(extio_dev_device))
{
device_destroy(extio_dev_class, MKDEV(major, 0));
unregister_chrdev(major, extio_dev_name);
return -1;
}
return 0;
}
static void __exit extio_dev_exit(void)
{
// timer_exit();
hrtimer_cancel(&kthread_timer);
printk("modlog: func %s, line %d.\n", __FUNCTION__, __LINE__);
gpio_set_value(LED_PIN, 1);
gpio_free(SERIAL_CLK_PIN);
gpio_free(SERIAL_LE_PIN);
gpio_free(SERIAL_PE_PIN);
gpio_free(SERIAL_IN_PIN);
gpio_free(SERIAL_OUT_PIN);
gpio_free(LED_PIN);
device_destroy(extio_dev_class, MKDEV(major, 0)); // 销毁设备节点,销毁后/dev/下设备节点文件就会删除
class_destroy(extio_dev_class);
unregister_chrdev(major, extio_dev_name); // 注销字符设备
}
module_init(extio_dev_init); // 模块入口
module_exit(extio_dev_exit); // 模块出口
MODULE_LICENSE("GPL"); // 模块许可
测试源码
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd,value;
if( argc !=4 ){
printf("usage: extio_app <devpath> <value> -w\n");
return -1;
}
fd = open(argv[1],O_RDWR);
if( fd<0 ){
printf("applog: can not open file %s\n",argv[1]);
return -1;
}
if( 0 == strcmp(argv[3],"-w") ){
write(fd,argv[2],3);
printf("applog: write - %s\n",argv[2]);
}
else{
close(fd);
return -1;
}
close(fd);
return 0;
}