2018年7月16日月曜日

使いそびれたパラレルキャラクタ液晶のSPI化(2/2)

前回からのつづき
制御用のFON2405E(LEDE・Openwrt)向けドライバ作成

1. SPI仕様

[0x01][コントラスト] ※コントラストは0x00〜0x06
[0x02][コマンド]  ※ RS=0 でLCDに出力
[0x03][データ]   ※ RS=1 でLCDに出力

http://akizukidenshi.com/download/ds/sunlike/SD1602HULB-XA-G-G.PDF
http://219.117.208.26/~saka/ham/LCD2/

2. ドライバ仕様

基本的には、MCP23S09の時と同じ。
キャラクタデバイスとして[/dev/ PicClcdDriver0]が作成される。
これに表示したい内容を流しこめばOK
一方で、各種制御は[/sys/bus/spi/drivers/PicClcdDriver/spi0.1/]に
lcd_clear:Clear Display(全表示クリア)
lcd_set_cgram:Set CGRAM address
lcd_set_ddram:Set DDRAM address
が作成される

3. 動作

insmod pic_clcd.ko
echo > /sys/bus/spi/drivers/PicClcdDriver/spi0.1/lcd_clear
echo -n HelloWorld! >  /dev/ PicClcdDriver0

英数字以外 例えば 「イ」とか
echo -ne "\xB2" > /dev/ PicClcdDriver0

外字CGRAM
0番目に「晴?」アイコン
echo 00411040E04110400 > /sys/bus/spi/drivers/PicClcdDriver/spi0.1/lcd_set_cgram



ドライバソースコード
/*
* pic_cled.c
*
* Created on: 2018/07/08
* Author: adeno
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/uaccess.h> //copy_from_user
#include <linux/gpio.h>
#include <linux/spi/spi.h>
MODULE_AUTHOR("Adeno");
MODULE_LICENSE("GPL v2");
//=======================================================================================
/* ドライバ */
#define DRV_NUM_DEVS 1 /* このドライバが制御するデバイスの数 */
#define DRV_DEVNAME "PicClcdDriver" /* このデバイスドライバの名称 */
#define DRV_MAJOR 0 /* メジャー番号だが自動設定なので0 */
#define DRV_MINOR 0 /* マイナー番号のベース番号 */
#define DRV_GPIO_MAPNAME "pcd_gpio_map"
/* SPI PIC設定 */
#define SPCD_PACKET_SIZE 1
const uint8_t SPCD_LCD_PWM = 0x01;
const uint8_t SPCD_LCD_CMD = 0x02;
const uint8_t SPCD_LCD_DATA = 0x03;
const uint8_t SPCD_LCD_CMD_CLEARDISPLAY = 0b00000001;
const uint8_t SPCD_LCD_CMD_RETURNHOME = 0b00000010;
const uint8_t SPCD_LCD_CMD_SETCGRAM = 0b01000000;
const uint8_t SPCD_LCD_CMD_SETDDRAM = 0b10000000;
//=======================================================================================
/* ドライバ */
static int _spcd_major = DRV_MAJOR;
static int _spcd_minor = DRV_MINOR;
/* SPI */
struct drvdata {
struct spi_device *spi;
struct mutex lock;
unsigned char tx[SPCD_PACKET_SIZE];
unsigned char rx[SPCD_PACKET_SIZE];
struct spi_transfer xfer;
struct spi_message msg;
dev_t md_dev;
struct class *drv_class;
struct cdev *drv_cdev_array;
} ____cacheline_aligned;
static struct drvdata spcd;
static struct spi_board_info spcd_info = {
.modalias = "spcd",
.max_speed_hz = 1000000,
.bus_num = 0,
.chip_select = 0,
.mode = SPI_MODE_3,
};
static struct spi_device_id spcd_id[] = {
{ "spcd", 0 },
{ },
};
MODULE_DEVICE_TABLE(spi, spcd_id);
/* Parameters Setting */
static int gpiono = 9;
static int show_debug = 0;
//static int hexmode = 0;
//=======================================================================================
/* プロトタイプ宣言 */
static unsigned int spcd_set_value( struct drvdata *data,unsigned int v);
static void spi_remove_device(struct spi_master *master, unsigned int cs);
static int spcd_probe(struct spi_device *spi);
static int spcd_remove(struct spi_device *spi);
static int spcd_open(struct inode *inode, struct file *filep);
static int spcd_release(struct inode *inode, struct file *filep);
static ssize_t spcd_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos);
static int spcd_register_dev(void);
static int spcd_init(void);
static void spcd_exit(void);
//=======================================================================================
/* SPIデバイス */
static struct spi_driver spcd_driver = {
.driver = {
.name = DRV_DEVNAME,
.owner = THIS_MODULE,
},
.id_table = spcd_id,
.probe = spcd_probe,
.remove = spcd_remove,
};
/* キャラクタデバイス */
struct file_operations spcd_fops = {
.open = spcd_open,
.release = spcd_release,
.write = spcd_write,
};
//=======================================================================================
static int lcd_clear_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int size = strlen( buf );
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev);
spcd_set_value(drd,SPCD_LCD_CMD);
spcd_set_value(drd,SPCD_LCD_CMD_CLEARDISPLAY);
// spcd_set_value(drd,SPCD_LCD_FIN);
return size;
}
static DEVICE_ATTR(lcd_clear, S_IWUSR|S_IWGRP, NULL, lcd_clear_store );
static int lcd_set_cgram_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
//CharNo Line(0-7)
//[1] [2]x8
int b[9],i;
int size = strlen( buf );
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev);
if(size >= 17){
if(buf[0] >='0' && buf[0] <= '9') b[0] = buf[0]-'0';
b[0] = SPCD_LCD_CMD_SETCGRAM | (((b[0] & 0b111) << 3) & 0b111000);
if(show_debug) printk(KERN_INFO "LCD SET CG Adr size=%d,C=%d\n",size,b[0]);
// /*if(show_debug)*/ printk(KERN_INFO "LCD SET CG Adr output=%d\n",b[0]);
spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,b[0]);
for(i=0; i<8; i++){
if(buf[i*2+1] >='0' && buf[i*2+1] <= '9') b[i] = (buf[i*2+1]-'0') * 0x10;
else if(buf[i*2+1] >='A' && buf[i*2+1] <= 'F') b[i] += (buf[i*2+1]-'A'+0xA) * 0x10;
else if(buf[i*2+1] >='a' && buf[i*2+1] <= 'f') b[i] += (buf[i*2+1]-'A'+0xA) * 0x10;
if(buf[i*2+2] >='0' && buf[i*2+2] <= '9') b[i] += (buf[i*2+2]-'0');
else if(buf[i*2+2] >='A' && buf[i*2+2] <= 'F') b[i] += (buf[i*2+2]-'A'+0xA);
else if(buf[i*2+2] >='a' && buf[i*2+2] <= 'f') b[i] += (buf[i*2+2]-'A'+0xA);
spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,b[i]);
}
// spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,0b10); //ホームに
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,buf[0]-'0');
//
//
//
// if((buf[0] >='0' && buf[0] <= '9') && (buf[1] >='0' && buf[1] <= '9')){
// int cno = buf[0]-'0';
// int lno = buf[1]-'0';
// int b = 0x40 | (((cno & 0b111) << 3) | (lno & 0b111));
// /*if(show_debug)*/ printk(KERN_INFO "LCD SET CG Adr size=%d,C=%d,L=%d,output=%d\n",size,cno,lno,b);
//
// spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,b);
//// spcd_set_value(drd,SPCD_LCD_FIN);
//
// //debug
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b01010);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b01010);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b01010);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101);
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b00000);
//// spcd_set_value(drd,SPCD_LCD_FIN);
// }
}
return size;
}
static DEVICE_ATTR(lcd_set_cgram, S_IWUSR|S_IWGRP, NULL, lcd_set_cgram_store );
static int lcd_set_ddram_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
//Adr
//[2]
int b;
int size = strlen( buf );
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev);
if(size >= 1){
if(buf[0] >='0' && buf[0] <= '9') b = (buf[0]-'0') * 0x10;
else if(buf[0] >='A' && buf[0] <= 'F') b += (buf[0]-'A'+0xA) * 0x10;
else if(buf[0] >='a' && buf[0] <= 'f') b += (buf[0]-'A'+0xA) * 0x10;
if(buf[1] >='0' && buf[1] <= '9') b += (buf[1]-'0');
else if(buf[1] >='A' && buf[1] <= 'F') b += (buf[1]-'A'+0xA);
else if(buf[1] >='a' && buf[1] <= 'f') b += (buf[1]-'A'+0xA);
/*if(show_debug)*/ printk(KERN_INFO "LCD SET DD Adr size=%d,C=%d\n",size,(b | SPCD_LCD_CMD_SETDDRAM));
spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,(b | SPCD_LCD_CMD_SETDDRAM));
}
return size;
}
static DEVICE_ATTR(lcd_set_ddram, S_IWUSR|S_IWGRP, NULL, lcd_set_ddram_store );
static int lcd_contrast_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int size = strlen( buf );
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev);
if(size > 0){
if(show_debug) printk(KERN_INFO "Change LCD Contrast %d,%c\n", size,buf[0]);
spcd_set_value(drd,SPCD_LCD_PWM);
switch(buf[0]){
case '0':
spcd_set_value(drd,0x00);
break;
case '1':
spcd_set_value(drd,0x01);
break;
case '2':
spcd_set_value(drd,0x02);
break;
case '3':
spcd_set_value(drd,0x03);
break;
case '4':
spcd_set_value(drd,0x04);
break;
case '5':
spcd_set_value(drd,0x05);
break;
case '6':
spcd_set_value(drd,0x06);
break;
default:
spcd_set_value(drd,0x03);
break;
}
}
return size;
}
static DEVICE_ATTR(lcd_contrast, S_IWUSR|S_IWGRP, NULL, lcd_contrast_store );
static unsigned int spcd_set_value( struct drvdata *data,unsigned int v)
{
unsigned int r = 0;
mutex_lock( &data->lock );
data->tx[0] = v;
if( spi_sync( data->spi, &data->msg) ) {
if(show_debug) printk(KERN_INFO "spi_sync_transfer returned non zero\n" );
}
usleep_range(1000,2000);
mutex_unlock(&data->lock);
return r;
}
/**
* SPIデバイスの削除
*/
static void spi_remove_device(struct spi_master *master, unsigned int cs)
{
struct device *dev;
char str[128];
snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs);
if(show_debug) printk(KERN_INFO "[spi_remove_device] %s\n",str);
// SPIデバイスを探す
dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
// あったら削除
if( dev ){
if(show_debug) printk(KERN_INFO "Delete %s\n", str);
device_del(dev);
}
}
/**
* SPI通信初期化
*/
static int spcd_probe(struct spi_device *spi)
{
// struct mcp_drvdata *data;
int retval;
if(show_debug) printk(KERN_INFO "spcd spi probe\n");
/* SPIを設定する */
spi->max_speed_hz = spcd_info.max_speed_hz;
spi->mode = spcd_info.mode;
spi->bits_per_word = 8;
if( spi_setup( spi ) ) {
printk(KERN_ERR "spi_setup returned error\n");
return -ENODEV;
}
spcd.spi = spi;
mutex_init( &spcd.lock );
spcd.xfer.tx_buf = spcd.tx;
spcd.xfer.rx_buf = 0;//data->rx;
spcd.xfer.bits_per_word = 8;
spcd.xfer.len = SPCD_PACKET_SIZE;
spcd.xfer.cs_change = 0;
spcd.xfer.delay_usecs = 0;
spcd.xfer.speed_hz = 1000000;
spi_message_init_with_transfers( &spcd.msg, &spcd.xfer, 1 );
spi_set_drvdata( spi, &spcd );
mutex_lock( &spcd.lock );
spcd.tx[0] = 0x00;
if( spi_sync( spcd.spi, &spcd.msg) ) {
printk(KERN_INFO "spi_sync_transfer returned non zero\n" );
}
mutex_unlock(&spcd.lock);
retval = device_create_file( &spi->dev, &dev_attr_lcd_clear );
if(retval) {
printk(KERN_ERR "failed to add lcd_clear attribute\n" );
}
retval = device_create_file( &spi->dev, &dev_attr_lcd_contrast );
if(retval) {
printk(KERN_ERR "failed to add lcd_contrast attribute\n" );
}
retval = device_create_file( &spi->dev, &dev_attr_lcd_set_cgram );
if(retval) {
printk(KERN_ERR "failed to add lcd_set_cgram attribute\n" );
}
retval = device_create_file( &spi->dev, &dev_attr_lcd_set_ddram );
if(retval) {
printk(KERN_ERR "failed to add lcd_set_ddram attribute\n" );
}
return 0;
}
/**
* SPI通信終了
*/
static int spcd_remove(struct spi_device *spi)
{
if(show_debug) printk(KERN_INFO "[spcd spi_remove]\n");
struct drvdata *drd = spi_get_drvdata(spi);
mutex_lock( &drd->lock );
drd->tx[0] = 0x00;
if( spi_sync( drd->spi, &drd->msg) ) {
printk(KERN_INFO "spi_sync_transfer returned non zero\n" );
}
mutex_unlock(&drd->lock);
device_remove_file( &spi->dev, &dev_attr_lcd_clear);
device_remove_file( &spi->dev, &dev_attr_lcd_contrast);
device_remove_file( &spi->dev, &dev_attr_lcd_set_cgram);
device_remove_file( &spi->dev, &dev_attr_lcd_set_ddram);
spi_set_drvdata(drd->spi, NULL);
drd->spi = NULL;
mutex_destroy(&spcd.lock);
if(gpiono != 0) gpio_free(gpiono);
return 0;
}
/**
* デバイスオープン時の処理
*/
static int spcd_open(struct inode *inode, struct file *filep)
{
if(show_debug) printk(KERN_INFO "[spcd_open]\n");
filep->private_data = &spcd;
return 0;
}
/**
* デバイスクローズ時の処理
*/
static int spcd_release(struct inode *inode, struct file *filep)
{
if(show_debug) printk(KERN_INFO "[spcd_release]\n");
struct drvdata *drd = filep->private_data;
if ((drd == NULL) || (drd->spi == NULL))
return -ENODEV;
filep->private_data = NULL;
return 0;
}
/**
* デバイス書き込み処理
*/
static ssize_t spcd_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos)
{
char cvalue;
// int i;
struct drvdata *drd = filep->private_data;
if ((drd == NULL) || (drd->spi == NULL))
return -ENODEV;
// struct mcp_drvdata *data = (struct mcp_drvdata *)dev_get_drvdata(filep->private_data);
if(count > 0) {
if(gpiono != 0) gpio_set_value(gpiono,1);
if(copy_from_user( &cvalue, buf, sizeof(char) )) {
return -EFAULT;
}
spcd_set_value(drd,SPCD_LCD_DATA);
spcd_set_value(drd,cvalue);
// spcd_set_value(drd,SPCD_LCD_FIN);
// if((cvalue >=0) && (cvalue <= 9)) spcd_set_value(drd,cvalue);
// else if(('0' <= cvalue) && (cvalue <= '9')) {
// spcd_set_value(drd,0x03);
// spcd_set_value(drd,cvalue);
// spcd_set_value(drd,0x00);
// }
// else{
// if(hexmode){
// if(('A' <= cvalue) && (cvalue <= 'F')) spcd_set_value(drd,cvalue - 'A' + 10);
// else if(('a' <= cvalue) && (cvalue <= 'f')) spcd_set_value(drd,cvalue - 'a' + 10);
// }
// else if(show_debug) printk(KERN_ALERT "Can not display %d\n", cvalue );
// }
if(gpiono != 0) gpio_set_value(gpiono,0);
return sizeof(char);
}
return 0;
}
/**
* デバイスドライバをカーネルに登録
*/
static int spcd_register_dev(void)
{
int retval;
// dev_t dev;
size_t size;
int i;
/* 空いているメジャー番号を使ってメジャー&
マイナー番号をカーネルに登録する */
retval = alloc_chrdev_region(
&spcd.md_dev, /* 結果を格納するdev_t構造体 */
DRV_MINOR, /* ベースマイナー番号 */
DRV_NUM_DEVS, /* デバイスの数 */
DRV_DEVNAME /* デバイスドライバの名前 */
);
if( retval < 0 ) {
printk(KERN_ERR "alloc_chrdev_region failed.\n" );
return retval;
}
_spcd_major = MAJOR(spcd.md_dev);
/* デバイスクラスを作成する */
spcd.drv_class = class_create(THIS_MODULE,DRV_DEVNAME);
if(IS_ERR(spcd.drv_class))
return PTR_ERR(spcd.drv_class);
/* cdev構造体の用意 */
size = sizeof(struct cdev) * DRV_NUM_DEVS;
spcd.drv_cdev_array = (struct cdev*)kmalloc(size, GFP_KERNEL);
/* デバイスの数だけキャラクタデバイスを登録する */
/* ただし7セグLEDは1個しかない */
for( i = 0; i < DRV_NUM_DEVS; i++ ) {
dev_t devno = MKDEV(_spcd_major, _spcd_minor+i);
/* キャラクタデバイスとしてこのモジュールをカーネルに登録する */
cdev_init(&(spcd.drv_cdev_array[i]), &spcd_fops);
spcd.drv_cdev_array[i].owner = THIS_MODULE;
if( cdev_add( &(spcd.drv_cdev_array[i]), devno, 1) < 0 ) {
/* 登録に失敗した */
printk(KERN_ERR "cdev_add failed minor = %d\n", _spcd_minor+i );
}
else {
/* デバイスノードの作成 */
device_create(
spcd.drv_class,
NULL,
devno,
NULL,
DRV_DEVNAME"%u",_spcd_minor+i
);
}
}
return 0;
}
/**
* モジュールの初期化処理
*/
static int spcd_init(void)
{
int retval;
int i;
/* 開始のメッセージ */
printk(KERN_INFO "%s loading...\n", DRV_DEVNAME );
if(gpiono != 0){
/* GPIOレジスタがマップ可能か調べる */
retval = gpio_request_one(gpiono, GPIOF_OUT_INIT_LOW, DRV_GPIO_MAPNAME);
if (retval) {
printk(KERN_ERR "Unable to request GPIOs: %d\n", retval);
return retval;
}
for( i=0; i<10; i++ ) {
gpio_set_value(gpiono,1);
msleep(10);
gpio_set_value(gpiono,0);
msleep(10);
}
}
/* デバイスドライバをカーネルに登録 */
retval = spcd_register_dev();
if( retval != 0 ) {
printk( KERN_ALERT "mcp7d driver register failed.\n");
return retval;
}
if(show_debug)
printk( KERN_INFO "mcp7d driver register sccessed.\n");
struct spi_master *master;
struct spi_device *spi_device;
spi_register_driver(&spcd_driver);
spcd_info.bus_num = 0;
spcd_info.chip_select = 1;
master = spi_busnum_to_master(spcd_info.bus_num);
if( ! master ) {
printk( KERN_ERR "spi_busnum_to_master returned NULL\n");
spi_unregister_driver(&spcd_driver);
return -ENODEV;
}
spi_remove_device(master, spcd_info.chip_select);
spi_device = spi_new_device( master, &spcd_info );
if( !spi_device ) {
printk(KERN_ERR "spi_new_device returned NULL\n" );
spi_unregister_driver(&spcd_driver);
return -ENODEV;
}
return 0;
}
/**
* モジュールの終了処理
*/
static void spcd_exit(void)
{
int i;
dev_t devno;
if(show_debug) printk(KERN_INFO "[spcd_exit]\n");
if(show_debug) printk(KERN_INFO "[cdev_del]\n");
/* キャラクタデバイスの登録解除 */
for( i = 0; i < DRV_NUM_DEVS; i++ ) {
cdev_del(&(spcd.drv_cdev_array[i]));
devno = MKDEV(_spcd_major, _spcd_minor+i);
device_destroy(spcd.drv_class, devno);
}
/* メジャー番号/マイナー番号を取り除く */
devno = MKDEV(_spcd_major,_spcd_minor);
unregister_chrdev_region(devno, DRV_NUM_DEVS);
/* デバイスノードを取り除く */
class_destroy( spcd.drv_class );
kfree(spcd.drv_cdev_array);
if(show_debug) printk(KERN_INFO "[spcd_del]\n");
spi_unregister_driver(&spcd_driver);
}
module_init(spcd_init);
module_exit(spcd_exit);
module_param( show_debug, int, S_IRUSR | S_IRGRP | S_IROTH );
module_param( gpiono, int, S_IRUSR | S_IRGRP | S_IROTH );
//module_param( hexmode, int, S_IRUSR | S_IRGRP | S_IROTH );
view raw pic_clcd.c hosted with ❤ by GitHub

0 件のコメント:

コメントを投稿