2016年10月23日日曜日

ラズベリー・パイで自力シリアル通信(ドライバー編)

随分と時間がかかってしまったけど、ようやく動くものになってきた。

概要
カーネルモジュール(ドライバー?)を作成して、GPIOをパタパタさせる。
8月からやっているヤツのデバドラ版。

デバイスとして扱われるので、echoやcatで入出力ができたり、既存の通信プログラムでも使えると思う。
出力されるクロックはdelayを使っているので、安定していると思う。
データのポーリングはカーネルタイマーを使っているので、
ラズベリー・パイの場合は100Hzが(10ms)が限界。
性能評価はこれから。

cat /dev/zero > /dev/MySFR9km0

とやってみると、大体350 Char/Secくらい。意外と遅い。

PIC側の設計上は960Char/Secまで行けるのはずなのだが??


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include "rpi_gpio.h"
MODULE_AUTHOR("Adeno");
MODULE_LICENSE("Dual BSD/GPL");
#define MYSFR9_NUM_DEVS 1 /* このドライバが制御するデバイスの数 */
#define MYSFR9_DEVNAME "MySFR9km" /* このデバイスドライバの名称 */
#define MYSFR9_MAJOR 0 /* メジャー番号だが自動設定なので0 */
#define MYSFR9_MINOR 0 /* マイナー番号のベース番号 */
#define MySFR9_GPIO_MAPNAME "mysfr9_gpio_map"
#define MYSFR_GPIO_BASE 7
#define UDELAY 50 //us
#define CHARDELAY 592 //us
#define RCVPOLL 10000 //us
#define RCV_QUEUE_SIZE 8 //SFR ->
#define SND_QUEUE_SIZE 8 // -> SFR
static int _mysfr9_major = MYSFR9_MAJOR;
static int _mysfr9_minor = MYSFR9_MINOR;
static struct cdev *mysfr9_cdev_array = NULL;
static struct class *mysfr9_class = NULL;
static void __iomem *gpio_map;
volatile uint32_t *gpio_base;
static struct timer_list poll_timer;
//RCV関係 SFR ->
static unsigned char ircv,rcv;
static unsigned char rcv_queue[RCV_QUEUE_SIZE];
static int rq_head, rq_tail;
//SND関係 -> SFR
static unsigned char isnd,snd;
static unsigned char snd_queue[SND_QUEUE_SIZE];
static int sq_head, sq_tail;
/*プロトタイプ宣言*/
//GPIOレジスタのマップ関係
static int mysfr9_gpio_map(void);
static int mysfr9_gpio_unmap(void);
static void mysfr9_gpio_setup(void);
static int rpi_gpio_function_set(int pin, uint32_t func);
static int rpi_gpio_pull_control(int pin, uint32_t pullmode);
static void rpi_gpio_set32( uint32_t mask, uint32_t val );
//static uint32_t rpi_gpio_get32( uint32_t mask );
static void rpi_gpio_clear32( uint32_t mask, uint32_t val );
static uint32_t rpi_gpio_getpin( int pin );
static void gpio_put_msb(unsigned char isend ,unsigned char send, unsigned char *ircv,unsigned char *rcv);
//カーネルへの登録関係
static int mysfr9_register_dev(void);
//デバイスに対する処理
static int mysfr9_open(struct inode *inode, struct file *filep);
static int mysfr9_release(struct inode *inode, struct file *filep);
static ssize_t mysfr9_write(struct file *filep,const char __user *buf, size_t count, loff_t *f_pos);
static ssize_t mysfr9_read(struct file *filep,char __user *buf,size_t count, loff_t *f_pos);
//File Operations構造体
struct file_operations mysfr9_fops = {
.open = mysfr9_open,
.release = mysfr9_release,
.write = mysfr9_write,
.read = mysfr9_read,
};
//タイマー関係
static void register_poll_timer(void);
static void poll_timer_handler(unsigned long data);
//QUEUE関係
static int ArrayEnqueue(unsigned char *queue, int data, int *head, int *tail, size_t n);
static unsigned char ArrayDequeue(unsigned char *queue, int *head, int *tail, size_t n);
///////////////////////////////////////////////////////////////////////////////////////////////////
//デバイスドライバがロードされた時の処理
static int mysfr9_init(void)
{
int retval;
/* 開始のメッセージ */
printk(KERN_INFO "%s loading...\n", MYSFR9_DEVNAME );
sq_head=0, sq_tail=0;
rq_head=0, rq_tail=0;
/* GPIOレジスタがマップ可能か調べる */
retval = mysfr9_gpio_map();
if( retval != 0 ) {
printk( KERN_ALERT "Can not use GPIO registers.\n");
return -EBUSY;
}
/* GPIO初期化 */
mysfr9_gpio_setup();
isnd=1; snd='I';
ircv=0; rcv=0;
gpio_put_msb(isnd,snd,&ircv,&rcv);
/* デバイスドライバをカーネルに登録 */
retval = mysfr9_register_dev();
if( retval != 0 ) {
printk( KERN_ALERT "MySFR9 driver register failed.\n");
return retval;
}
printk( KERN_INFO "MySFR9 driver register sccessed.\n");
/* GPIOレジスタのアンマップ */
mysfr9_gpio_unmap();
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//デバイスドライバがアンロードされた時の処理
static void mysfr9_exit(void)
{
int i;
dev_t devno;
del_timer_sync(&poll_timer);
/* キャラクタデバイスの登録解除 */
for( i = 0; i < MYSFR9_NUM_DEVS; i++ ) {
cdev_del(&(mysfr9_cdev_array[i]));
devno = MKDEV(_mysfr9_major, _mysfr9_minor+i);
device_destroy(mysfr9_class, devno);
}
/* メジャー番号/マイナー番号を取り除く */
devno = MKDEV(_mysfr9_major,_mysfr9_minor);
unregister_chrdev_region(devno, MYSFR9_NUM_DEVS);
/* デバイスノードを取り除く */
class_destroy( mysfr9_class );
/* GPIOレジスタのアンマップ */
mysfr9_gpio_unmap();
kfree(mysfr9_cdev_array);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//デバイスに対する処理
static int mysfr9_open(struct inode *inode, struct file *filep)
{
int retval;
if( gpio_base != NULL ) {
/* すでにオープンされている */
printk(KERN_ERR "MySFR9 is already open.\n" );
return -EIO;
}
retval = mysfr9_gpio_map();
if( retval != 0 ) {
printk(KERN_ERR "Can not open MySFR9.\n" );
return retval;
}
printk( KERN_INFO "RPI HZ = %d\n", HZ );
register_poll_timer();
return 0;
}
static int mysfr9_release(struct inode *inode, struct file *filep)
{
//まだSFRに送信できていないないものがあるので、待ってみる
while ( sq_tail - sq_head ) {
udelay(UDELAY);
}
del_timer_sync(&poll_timer);
mysfr9_gpio_unmap();
return 0;
}
static ssize_t mysfr9_write(struct file *filep,const char __user *buf, size_t count, loff_t *f_pos)
{
//char cvalue;
//int retval;
unsigned char c;
if(count > 0) {
if(copy_from_user( &c, buf, sizeof(char) )) {
return -EFAULT;
}
while(ArrayEnqueue(snd_queue, c, &sq_head, &sq_tail, SND_QUEUE_SIZE) == 0){
//udelay(UDELAY);
msleep(1);
}
//isnd=1;
//ircv=0; rcv=0;
//gpio_put_msb(isnd,snd,&ircv,&rcv);
/*retval = ssegled_put(cvalue);
if( retval != 0 ) {
printk(KERN_ALERT "Can not display %d\n", cvalue );
}
else {
ssegled_display_value = cvalue;
}*/
return sizeof(char);
}
return 0;
}
static ssize_t mysfr9_read(struct file *filep,char __user *buf,size_t count, loff_t *f_pos){
unsigned char c;
//printk( KERN_INFO "RCV READ cont=%d, RCVQ=%d\n",count,(rq_tail - rq_head));
if(count > 0) {
while((rq_tail - rq_head) == 0){
//udelay(UDELAY);
msleep(1);
}
c = ArrayDequeue(rcv_queue, &rq_head, &rq_tail, RCV_QUEUE_SIZE);
//printk( KERN_INFO "RCV READ char=%c\n",c);
if(copy_to_user( buf,&c, sizeof(char) )) {
return -EFAULT;
}
return sizeof(char);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//カーネルへの登録
static int mysfr9_register_dev(void)
{
int retval;
dev_t dev;
size_t size;
int i;
/* 空いているメジャー番号を使ってメジャー&
マイナー番号をカーネルに登録する */
retval = alloc_chrdev_region(
&dev, /* 結果を格納するdev_t構造体 */
MYSFR9_MAJOR, /* ベースマイナー番号 */
MYSFR9_NUM_DEVS, /* デバイスの数 */
MYSFR9_DEVNAME /* デバイスドライバの名前 */
);
if( retval < 0 ) {
printk(KERN_ERR "alloc_chrdev_region failed.\n" );
return retval;
}
_mysfr9_major = MAJOR(dev);
/* デバイスクラスを作成する */
mysfr9_class = class_create(THIS_MODULE,MYSFR9_DEVNAME);
if(IS_ERR(mysfr9_class))
return PTR_ERR(mysfr9_class);
/* cdev構造体の用意 */
size = sizeof(struct cdev) * MYSFR9_NUM_DEVS;
mysfr9_cdev_array = (struct cdev*)kmalloc(size, GFP_KERNEL);
/* デバイスの数だけキャラクタデバイスを登録する */
/* ただし7セグLEDは1個しかない */
for( i = 0; i < MYSFR9_NUM_DEVS; i++ ) {
dev_t devno = MKDEV(_mysfr9_major, _mysfr9_minor+i);
/* キャラクタデバイスとしてこのモジュールをカーネルに登録する */
cdev_init(&(mysfr9_cdev_array[i]), &mysfr9_fops);
mysfr9_cdev_array[i].owner = THIS_MODULE;
if( cdev_add( &(mysfr9_cdev_array[i]), devno, 1) < 0 ) {
/* 登録に失敗した */
printk(KERN_ERR "cdev_add failed minor = %d\n", _mysfr9_minor+i );
}
else {
/* デバイスノードの作成 */
device_create(
mysfr9_class,
NULL,
devno,
NULL,
MYSFR9_DEVNAME"%u",_mysfr9_minor+i
);
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//GPIOレジスタのマップ関係
static int mysfr9_gpio_map(void)
{
if( !request_mem_region(RPI_GPIO_BASE,
RPI_BLOCK_SIZE,
MySFR9_GPIO_MAPNAME) ) {
printk( KERN_ALERT "request_mem_region failed.\n");
//return -EBUSY;
}
gpio_map = ioremap_nocache(RPI_GPIO_BASE, BLOCK_SIZE);
gpio_base = (volatile uint32_t *)gpio_map;
return 0;
}
static int mysfr9_gpio_unmap(void)
{
iounmap(gpio_map);
release_mem_region(RPI_GPIO_BASE, RPI_BLOCK_SIZE);
gpio_map = NULL;
gpio_base = NULL;
return 0;
}
static void mysfr9_gpio_setup(void)
{
rpi_gpio_function_set(MYSFR_GPIO_BASE+0, RPI_GPF_OUTPUT);//CLK-OUT
rpi_gpio_function_set(MYSFR_GPIO_BASE+1, RPI_GPF_OUTPUT);//DATA-OUT
rpi_gpio_function_set(MYSFR_GPIO_BASE+2, RPI_GPF_INPUT);//DATA-INPUT
rpi_gpio_pull_control(MYSFR_GPIO_BASE+2,RPI_GPIO_PULLUP);
}
static int rpi_gpio_function_set(int pin, uint32_t func)
{
int index = RPI_GPFSEL0_INDEX + pin / 10;
uint32_t mask = ~(0x7 << ((pin % 10) * 3));
gpio_base[index] = (gpio_base[index] & mask) | ((func & 0x7) << ((pin % 10) * 3));
return 1;
}
static int rpi_gpio_pull_control(int pin, uint32_t pullmode)
{
gpio_base[RPI_GPPUD_INDEX] = pullmode & 0x03;
gpio_base[RPI_GPPUDCLK0_INDEX] = 0x01 << pin;
// wait > 150 cycles
udelay(100);
gpio_base[RPI_GPPUDCLK0_INDEX] = 0;
gpio_base[RPI_GPPUD_INDEX] = 0;
return 1;
}
static void rpi_gpio_set32(uint32_t mask, uint32_t val )
{
gpio_base[RPI_GPSET0_INDEX] = val & mask;
}
/*static uint32_t rpi_gpio_get32( uint32_t mask )
{
return gpio_base[RPI_GPLEV0_INDEX] & mask;
}*/
static void rpi_gpio_clear32(uint32_t mask, uint32_t val )
{
gpio_base[RPI_GPCLR0_INDEX] = val & mask;
}
static uint32_t rpi_gpio_getpin(int pin )
{
return (gpio_base[RPI_GPLEV0_INDEX] & (1 << pin)) != 0;
}
static void gpio_put_msb(unsigned char isend ,unsigned char send, unsigned char *ircv,unsigned char *rcv)
{
volatile int i=0;
volatile unsigned char _rcv = 0;
volatile unsigned char _send = send;
unsigned int _ircv = 0;
unsigned int rcvbit=0;
//D0 データ有無ビット
//--DATA
if(isend){
rpi_gpio_set32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE);
}else{
rpi_gpio_clear32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE);
}
//--CLK=H
rpi_gpio_set32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE);
udelay(UDELAY);
//--データ有無ビット入力
_ircv = rpi_gpio_getpin(MYSFR_GPIO_BASE+2);
//--CLK=L
rpi_gpio_clear32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE);
udelay(UDELAY);
//printf("データ有無\t送信:%d(%c)\t受信:%d(%c)\n",isend,((isend>0) ? 'X':'-'),_ircv,((_ircv>0) ? 'X':'-'));
for(i=0;i<8;i++){
//D1-9 データビット
//--データ出力
//printf("i=%d,v=%#x,v2=%d\n",i,v,((v & 0x80) == 0x80 ? 1 : 0));
//printf("%d",((v & 0x80) == 0x80 ? 1 : 0));
if((_send & 0x80) == 0x80){
rpi_gpio_set32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE);
}else{
rpi_gpio_clear32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE);
}
//--CLK=H
rpi_gpio_set32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE);
udelay(UDELAY);
//--データ入力
_rcv = _rcv << 1 ;
rcvbit = rpi_gpio_getpin(MYSFR_GPIO_BASE+2);
//printf("c=%d,gpin=%d\n",c,gpin);
if(rcvbit > 0){
_rcv = _rcv | 0x01;
}
_send=_send<<1;
//--CLK=L
rpi_gpio_clear32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE);
udelay(UDELAY);
}
*ircv = _ircv;
*rcv = _rcv;
//printf(" 内容\t送信:%c(%#x)\t受信:%c(%#x)\n",send,send,*rcv,*rcv);
//printf("----------------------------------------------------------------------------------------------------\n");
//printf("### 送信:%c - %c(%#x) 受信:%c - %c(%#x)\n",((isend>0) ? 'O':'X'),send,send,((_ircv>0) ? 'O':'X'),_rcv,_rcv);
udelay(CHARDELAY);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//タイマー処理関係
static void register_poll_timer(void)
{
init_timer(&poll_timer);
//poll_timer.expires = jiffies + HZ;
poll_timer.expires = jiffies + (HZ * RCVPOLL) / 1000000;
/* 最短のTickより短い場合は最短のTickに */
if( poll_timer.expires == 0 ) poll_timer.expires = jiffies + 1;
poll_timer.data = jiffies;
poll_timer.function = poll_timer_handler;
add_timer(&poll_timer);
}
static void poll_timer_handler(unsigned long data)
{
//unsigned char ircv,rcv;
//unsigned char isnd,snd;
do{
if(sq_tail - sq_head){
isnd = 1;
snd = ArrayDequeue(snd_queue, &sq_head, &sq_tail, SND_QUEUE_SIZE);
}else{
isnd = 0;
}
gpio_put_msb(isnd,snd,&ircv,&rcv);
if(ircv > 0){
//printk( KERN_INFO "RCV %c \n",rcv);
if(ArrayEnqueue(rcv_queue, rcv, &rq_head, &rq_tail, RCV_QUEUE_SIZE) > 0){
}else{
printk( KERN_ALERT "RCV Buffer Full.\n");
}
}
}while ((sq_tail - sq_head) || (ircv > 0));
/*while ((q_tail - q_head) || (ircv > 0)) {
if(q_tail - q_head){
isnd = 1;
snd = ArrayDequeue(queue, &q_head, &q_tail, QUEUE_SIZE);
}else{
isnd = 0;
}
gpio_put_msb(isnd,snd,&ircv,&rcv);
isnd = 0;
}*/
//isnd=1; snd='P';
//ircv=0; rcv=0;
//gpio_put_msb(isnd,snd,&ircv,&rcv);
//isnd=0;
register_poll_timer();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//Queue
static int ArrayEnqueue(unsigned char *queue, int data, int *head, int *tail, size_t n) {
if (*head % n != (*tail + 1) % n) {
queue[(*tail)++ % n] = data;
return *tail - *head;
} else {
return 0;
}
}
static unsigned char ArrayDequeue(unsigned char *queue, int *head, int *tail, size_t n) {
if (*head != *tail) {
return queue[(*head)++ % n];
} else {
return (unsigned char)0;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//initの登録
module_init(mysfr9_init);
//exitの登録
module_exit(mysfr9_exit);
view raw mysfr9km.c hosted with ❤ by GitHub

メモ
request_mem_regionが毎回失敗するのはなぜだろう
早速Amisでも移植してみるか?