2015年3月30日月曜日

FON2405EのGPIOでキャラクタ液晶を制御する

先週からGPIOの出力が一部間延びしてしまうことで、行き詰っていた。
気分を変えて、同期シリアル通信でキャラクタ液晶を制御したいと考えた。

結論から書くと、以下の2点の課題から、表示速度はすこぶる遅い。
1.仲介役のPICマイコンの動作速度は4MHz(内臓のオシレーターを使ったため)
2.やっぱりGPIO出力が間延びするから1クロック4msのディレーを入れた

 16文字のデータを送るのに、現状1.3秒かかる。
いざまとめるとこんなものなのだけど、なかなか時間がかかったし、
間延び問題が気になる・・・。



 ハード
FON2405EのGPIO---CLK,DATA---PICマイコン---キャラクタ液晶



ソフト
1.gpioのドライバ
2.gpioのアプリ
3.PICマイコンのファーム

1.gpioのドライバ
  標準のものから変更なし
  RALINK_GPIO_WRITE_BYTEを使用。
  今までの試行錯誤は何だったんだ・・・・。

2.gpioのアプリ
  変更点
  オプション[s]を追加
  s  <gpio clk> <gpio data> <delaytime> <char>
  これを指定するとgptio_test_write_srを呼ぶようにした。
  usleepを使うと4ms以下にはならないようだし、
  ドライバの方で、udelayを使って短くしても、
  前回の間延び問題のように不安定になってしまう。
  なので、CLKの変化のタイミングだけusleepを使って、
  DATAのセット部分はディレーなしにした。
  多少は速くなった。
void gpio_test_write_sr(int gpio_clk, int gpio_data,int delaytime, unsigned char *c){
gpio_write_int(RALINK_GPIO_DATA_MASK);
int i;
printf("gpio_write_sr Start:CLK=%d DATA=%d\n",gpio_clk,gpio_data);
struct timeval s,f;
gettimeofday(&s,NULL);
for(i=0;i<17;i++){
if(*(c+i) == '\0'){ break;}
gpio_write_byte3(gpio_clk,gpio_data,*(c+i));
usleep(delaytime); //10ms
//sleep(1);
}
gettimeofday(&f,NULL);
printf("Total Time=%dms\n",(f.tv_sec-s.tv_sec)*1000+(f.tv_usec-s.tv_usec)/1000);
}
view raw gistfile1.c hosted with ❤ by GitHub
int gpio_write_byte3(int port_clk,int port_data,unsigned char c){
int idx = 1, fd, req, i, value, tmp, udelay;
int shift_clk = port_clk - 8;
int shift_data = port_data - 8;
unsigned char mask = 1<<7;
struct timeval s,f;
//gettimeofday(&s,NULL);
udelay = RALINK_GPIO_UDELAY;
fd = open(GPIO_DEV, O_RDONLY);
if (fd < 0) {
perror(GPIO_DEV);
return -1;
}
//printf("shift_clk=%d shift_data=%d\n",shift_clk,shift_data);
if (0L <= idx && idx < RALINK_GPIO_DATA_LEN/8 && shift_clk >= 0 && shift_data >= 0)
req = RALINK_GPIO_WRITE_BYTE | (idx << RALINK_GPIO_DATA_LEN);
else {
close(fd);
printf("gpio_write_byte: index %d out of range\n", idx);
}
printf("target data:%c\n",c);
for(i=0;i<8;i++){
//gettimeofday(&s,NULL);
value = 0;
tmp = 0;
if((c & mask)>0){ value = 1;}
else { value = 0;}
//printf("target data:%d\n",value);
//clk reset(L) 1
tmp = (1<<shift_clk);
tmp &= 0xff;
if (ioctl(fd, req, tmp) < 0) { perror("ioctl"); close(fd); return -1; }
//printf("clk L\n");
//if (ioctl(fd, udelay,delaytime) < 0) { perror("ioctl"); close(fd); return -1;}
//usleep(500); //4ms
//data set
tmp = (1<<shift_clk);
tmp |= (value<<shift_data);
tmp &= 0xff;
if (ioctl(fd, req, tmp) < 0) { perror("ioctl"); close(fd); return -1; }
//printf("data set\n");
//usleep(500);
//if (ioctl(fd, udelay,delaytime) < 0) { perror("ioctl"); close(fd); return -1;}
//clk reset(H) 0
tmp = (value<<shift_data);
tmp &= 0xff;
if (ioctl(fd, req, tmp) < 0) { perror("ioctl"); close(fd); return -1; }
//printf("clk L->H\n");
usleep(500); //4ms
//if (ioctl(fd, udelay,delaytime) < 0) { perror("ioctl"); close(fd); return -1;}
//clk set(L) 1
tmp = (1<<shift_clk);
tmp |= (value<<shift_data);
tmp &= 0xff;
if (ioctl(fd, req, tmp) < 0) { perror("ioctl"); close(fd); return -1; }
//printf("clk H->L\n");
usleep(500); //4ms target sampling time = 500us * 3 = 1500us
//if (ioctl(fd, udelay,delaytime) < 0) { perror("ioctl"); close(fd); return -1;}
mask = mask >>1;
//gettimeofday(&f,NULL);
//printf("Total Time=%dms\n",(f.tv_sec-s.tv_sec)*1000+(f.tv_usec-s.tv_usec)/1000);
}
close(fd);
return 0;
}
view raw gistfile1.c hosted with ❤ by GitHub


3.PICマイコンのファーム
  変更点
  1ms毎にタイマー割り込みを行って、
  CLKがH→L(立ち下がり)のタイミングを探す。
  更に1ms経過してもLの状態だったら、DATAを読み込む。
main.c
/*
* File: main.c
* Author: adeno
*
* Created on 2014/12/07, 13:14
* RT3050F接続のテスト用
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "lcd_lib.h"
#include "rcv_lib.h"
/*
*
*/
// CONFIG
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = ON // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = ON // Low-Voltage Programming Enable bit (RB4/PGM pin has PGM function, low-voltage programming enabled)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#define _XTAL_FREQ 4000000 //delay用に必要(クロック4MHzを指定)
#define TMR0_CNT 252 //TMR0カウント値設定
#define CLK_PORT RB5
#define DATA_PORT RB7
#define LCD_ROW 16
#define LCD_COL 2
#define RCV_TIMEOUT 10 //受信タイムアウト 65ms * 10
#define DIST_UART 1
#define DIST_LCD 0
unsigned char dist; //標準出力の出力先
void putch(unsigned char c){
if(dist == DIST_LCD)
lcd_data(c);
else if(dist == DIST_UART){
while(!TXIF)
continue;
TXREG = c;
}
return;
}
//LCD用表示バッファ
volatile char lcd_buf[LCD_ROW+1];
volatile unsigned char lcd_poi = 0;
volatile unsigned char lcd_col_poi = 1; //書き込み行数
//SRの
unsigned char clk_port_buf;
unsigned char sr_timeoutcnt = 0;
volatile unsigned char sr_bit = 0;
volatile unsigned char sr_buf;
int main(int argc, char** argv) {
//PORTA 0,1,2,3 LCD DATA
//PORTA 7 E
//PORTA 6 RS
//PORTB 1 RX
//PORTB 2 TX
//PORTB 4 Debug
//PORTB 5 Input(CLK)
//PORTB 7 Input(DATA)
//PORTB 0,3 外部IO
PORTA = 0b00000000; //PORTAの中身をきれいにする
TRISA = 0b00000000; //PORTAは 1:入力 0:出力
PORTB = 0b01000101; //PORTBの中身をきれいにする
TRISB = 0b10110010; //PORTBは 1:入力 0:出力
//シリアル通信(ボーレート)
SYNC = 0; //非同期
BRGH = 1; //高速サンプル
SPBRG = 25; //4,000,000Hz / (16 * Baud) - 1
//
//シリアル送信
CSRC = 1;
TX9 = 0;
TXEN = 1;
//シリアル受信
SPEN = 1;
RX9 = 0;
CREN = 1;
ADEN = 0;
FERR = 0;
OERR = 0;
RX9D = 0;
//TMR0の割り込み初期設定
//0.25us(4MHz)*4*プリスケーラ−256 = 256us
//TMR0hは255-3 = 252
OPTION_REG = 0b0111; //プリスケーラ値設定0b0111(=256回)
TMR0 = TMR0_CNT; //TMR0カウント値設定
clk_port_buf = 0;
sr_timeoutcnt = 0;
//LCDの初期化
lcd_init();
//LCDの表示クリア
lcd_clear();
dist = DIST_LCD;
printf("Hello World!");
//lcd_cmd(0xc0);
//lcd_data('a');
//232に出力
dist = DIST_UART;
printf("Hello World!\r\n");
//受信バッファの準備
rcv_init();
//LCDバッファの準備
for(lcd_poi = 0; lcd_poi < LCD_ROW; lcd_poi++){
lcd_buf[lcd_poi] = ' ';
}
lcd_buf[LCD_ROW] = '\0';
lcd_poi = 0;
///
lcd_cmd(0xc0);
dist = DIST_LCD;
printf(lcd_buf);
PEIE = 1;
INTCONbits.TMR0IE =1; //タイマ割込み許可
RCIE = 1; //RX割り込みをイネーブル
INTCONbits.GIE = 1; //全体割込み許可
while(1) {
PORTB = 0b00001010;
__delay_ms(100);
PORTB = 0b00000010;
__delay_ms(100);
if(getRCVflg() == 1){
//C:コマンド
// C:clear 全クリア
//I:文字列追加
//N:文字列新規
//if(rcv_buf[0] == 'N' && rcv_buf[1] == ':'){
//以降は文字列
for(lcd_poi = 0; lcd_poi < LCD_ROW; lcd_poi++){
if(read_byte(lcd_poi) != '\n' ){
lcd_buf[lcd_poi] = read_byte(lcd_poi);
}else{
break;
}
}
for(;lcd_poi < LCD_ROW; lcd_poi++){
lcd_buf[lcd_poi] = '.';
}
lcd_change_col(lcd_col_poi);
//lcd_cmd(0xc0);
dist = DIST_LCD;
printf(lcd_buf);
/*}else if(rcv_buf[0] == 'C' && rcv_buf[1] == ':'){
}*/
clearRCVflg();
//RCIE = 1; //RX割り込み再開
}
//lcd_cmd(0xc0);
}
return (EXIT_SUCCESS);
}
void interrupt isr(void) //割込み関数
{
if (RCIF)
{
//rx_data = RCREG;
//受信タイムアウトフラグセット
//rcv_timeoutflg = 1;
//rcv_timeout = RCV_TIMEOUT;
unsigned char rcv_wk = ' ';
if(FERR)
{
rcv_wk = RCREG;
//rx_data = 'F';
}else if(OERR)
{
CREN = 0;
NOP();
CREN = 1;
//rx_data = 'O';
}else{
dist = DIST_UART;
rcv_byte(RCREG);
}
}else if(INTCONbits.TMR0IF == 1) { //割込み種がTimer0割込みの場合
INTCONbits.TMR0IF = 0; //Timer0割り込みフラグクリアー
TMR0 = TMR0_CNT; //TMR0カウント値設定
sr_timeoutcnt++;
if(sr_timeoutcnt > 250 && sr_bit > 0){
sr_bit = 0;
sr_timeoutcnt = 0;
dist = DIST_UART;
printf("TIMEOUT:%c\r\n",sr_buf);
}
if(CLK_PORT == 0){
if(clk_port_buf == 1){
//clk high
//DATA READ
clk_port_buf = 2;
sr_buf = (sr_buf<<1) | (0x01 & DATA_PORT);
sr_bit++;
sr_timeoutcnt = 0;
if(sr_bit > 7){
sr_bit = 0;
dist = DIST_UART;
rcv_byte(sr_buf);
/*if(DATA_PORT == 1)
printf("CLK H->L:DATA H\r\n");
else
printf("CLK H->L:DATA L\r\n");*/
}
}else if(clk_port_buf > 1){
}else{
clk_port_buf = 1;
}
}else{
clk_port_buf = 0;
}
/*if(rcv_timeoutflg == 1){
if(rcv_timeout == 0){
//終了条件3:タイムアウト?
//受信データの破棄
rcv_state = RCV_STATE_S1_CMDWAIT;
dist = DIST_UART;
printf("Rcv Timeout\r\n");
}else{
rcv_timeout--;
}
}*/
}
}
view raw main.c hosted with ❤ by GitHub
rcv_lib.c
#include <stdio.h>
#include "rcv_lib.h"
#include "lcd_lib.h"
volatile unsigned char rcv_state = 0;
volatile unsigned char rcv_poi = 0;
volatile unsigned char rcv_cmd = ' ';
volatile unsigned char rcv_buf[RCV_SIZE];
volatile unsigned char rcv_timeoutflg = 0;
volatile unsigned char rcv_timeout = 0;
volatile unsigned char rcv_full = 0;
void rcv_init(){
//受信バッファの準備
for(rcv_poi = 0; rcv_poi < RCV_SIZE; rcv_poi++){
rcv_buf[rcv_poi] = ' ';
}
rcv_full = 0;
}
char read_byte(int i){ return rcv_buf[i]; }
void clearRCVflg(){ rcv_full = 0; }
char getRCVflg(){ return rcv_full; }
void rcv_byte(unsigned char c){
if(rcv_full == 1){
printf("BufferFull>%c\r\n",c);
}else if(rcv_state == RCV_STATE_S1_CMDWAIT){
rcv_poi = 0;
if(c == 'C'){
rcv_timeoutflg = 0;
rcv_state = RCV_STATE_S2_CMDWAIT2;
//dist = DIST_UART;
printf("RCV>C\r\n");
//return "RCV>C\r\n";
}else if(c == 'N'){
rcv_timeoutflg = 0;
rcv_state = RCV_STATE_S3_DATAWAIT;
//dist = DIST_UART;
printf("RCV>N\r\n");
//return "RCV>N\r\n";
}else{
//dist = DIST_UART;
printf("Wait for CommandCode. But Rcv>%c\r\n",c);
//return "Wait for CommandCode. But Rcv>"+c+"\r\n";
}
}else if(rcv_state == RCV_STATE_S2_CMDWAIT2){
//コマンドを一時保存
rcv_timeoutflg = 0;
rcv_cmd = c;
rcv_state = RCV_STATE_S4_CMDFINWAIT;
//dist = DIST_UART;
printf("Command RCV>%c\r\n",c);
//return "Command RCV>"+c+"\r\n";
}else if(rcv_state == RCV_STATE_S4_CMDFINWAIT){
if(c == '>'){
rcv_timeoutflg = 0;
//コマンドの終了条件
if(rcv_cmd == 'C'){
//全クリア
lcd_clear();
}else if(rcv_cmd == '1'){
//カーソルを1行目に
lcd_change_col(0);
}else if(rcv_cmd == '2'){
//カーソルを2行目に
lcd_change_col(1);
}
//dist = DIST_UART;
printf("done\r\n");
rcv_state = RCV_STATE_S1_CMDWAIT;
//return "done\r\n";
}else{
//dist = DIST_UART;
printf("Wait for '>'. But Rcv>%c\r\n",c);
//return "Wait for '>'. But Rcv>"+c+"\r\n";
}
}else if(rcv_state == RCV_STATE_S3_DATAWAIT){
//dist = DIST_UART;
printf("Data RCV>%c poi:%d\r\n",c,rcv_poi);
if(c == '\n'){
//終了条件1:\n
rcv_buf[rcv_poi] = c;
//RCIE = 0; //RX割り込み一時停止
rcv_poi = 0;
rcv_timeoutflg = 0;
rcv_state = RCV_STATE_S1_CMDWAIT;
//rx_flg = 1;
rcv_full = 1;
//dist = DIST_UART;
printf("RCV FIN 1\r\n");
}else if(c == '\r'){
//\rは無視
rcv_timeoutflg = 0;
}else{
rcv_timeoutflg = 0;
rcv_buf[rcv_poi] = c;
rcv_poi++;
//終了条件2:バッファがいっぱい
if(rcv_poi >= RCV_SIZE){
//RCIE = 0; //RX割り込み一時停止
rcv_poi = 0;
rcv_state = RCV_STATE_S1_CMDWAIT;
//rx_flg = 1;
rcv_full = 1;
//dist = DIST_UART;
printf("RCV FIN 2\r\n");
}
}
}
}
view raw rcv_lib.c hosted with ❤ by GitHub

2015年3月23日月曜日

FON2405EのGPIOでなんちゃってシリアル通信

GPIOの制御が少しわかってきたので、
前回、同期シリアル通信の下準備を行った。

汎用性とか考えると、やっぱりUART(調歩シリアル通信)をやってみたくなり、
ドライバ部分を書き換えてみた。

実験なので、通信速度は9600bpsで、パリティなし、ストップビットは1ビットとした。
やっていることは、結構単純で、1クロック分=1^6μs ÷9600=104μs分ごとにデータを出力するというもの。

http://www5b.biglobe.ne.jp/~kouta_y/news/newsvb/vb14.html

まず、比較のためにFT232RLにて'a'の1文字を送信した場合
調べたとおり、104μsになっていた。

これを目指して、作ってみよう。
linux-2.6.21.x/drivers/char/ralink_gpio.cに追記
80行目付近にある[int ralink_gpio_ioctl]のcase文に

case RALINK_GPIO_UART_SEND:
   base_delay = 104;
           c = (unsigned char)(arg & 0xFFl);
        if (0L <= idx && idx < RALINK_GPIO_DATA_LEN) {
            tmp =le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
            //H
            tmp2 = tmp | (1L << idx);
            *(volatile u32 *)(RALINK_REG_PIODATA)= cpu_to_le32(tmp2);
            udelay(base_delay*3);
           
            //start_bit(L)
            tmp2 = tmp & ~(1L << idx);
            *(volatile u32 *)(RALINK_REG_PIODATA)= cpu_to_le32(tmp2);
            udelay(base_delay);

            //data loop
            for(i=0; i<8; i++){
                if((c & mask) > 0){
                    //h
                    tmp2 = tmp | (1L << idx);
                }else{
                    //l
                    tmp2 = tmp & ~(1L << idx);
                }
                            *(volatile u32 *)(RALINK_REG_PIODATA)= cpu_to_le32(tmp2);
                        udelay(base_delay);
                mask = mask <<1;
            }
            //stop bit
                tmp2 = tmp | (1L << idx);
            *(volatile u32 *)(RALINK_REG_PIODATA)= cpu_to_le32(tmp2);
            udelay(base_delay*3);
        }else
            return -EINVAL;
            break;

この結果が以下の通り
まぁなんとかそれっぽいものがでてきた。

いざ受信させて見ると、10回に1回くらい取りこぼしてしまう。
 (成功の場合)


(失敗の場合)


データの中間くらいが間延びしているように見える・・・。
なーぜーー







2015年3月15日日曜日

FON2405EのGPIOの速度(かなり改善)

昨日すこし改善できたGPIOの速度。
もう少し何とかならないかと、探していたら
こんなHPを発見
http://d.hatena.ne.jp/naoya/20080122/1200960926

http://proger.blog10.fc2.com/blog-entry-64.html

なるほど合点です。4msより早くならない理由はusleepが原因でしたか。

早速、usleepを削除してみると、4msが1.3μsになりました!!
つまり1周期2.6μsだから384KHz

なので、8bit分送るのには18.4μs!
ざっくり16文字×2行の液晶に表示する場合は、 588μsこれなら全く問題にならないね!



 ちなみに、ドライバー部分で無理やりループした場合は、

 1周期が400nsなので2.5MHz!
チョッ速になった。これでPICとの高速通信も問題なくできそう



2015年3月14日土曜日

FON2405EのGPIOの速度(すこし改善)

gpioコマンドを眺めていたら、
LED用のオプション「l」の他に
テストとして「w」があることにやっと気づいた。

そのままだと、1秒おきに1〜23までのGPIOの出力を変化させるだけなので、
少し変更して、特定のGPIOポートをパタパタさせるようにした。

user/rt2880_app/gpio内のgpio.c
gpio_test_write関数のgpio_write_initの後に
for(i=0; i< 1000; i++){
    gpio_write_bit(11,0);
    usleep(1);
    gpio_write_bit(11,1);
    usleep(1);
}

というのを挿入して、コンパイル。

 /opt/buildroot-gcc342/bin/mipsel-linux-gcc \
-I/home/adeno/fongpio/sdk3301/RT288x_SDK/source/linux-2.6.21.x/drivers/char \
-I/home/adeno/fongpio/sdk3301/RT288x_SDK/source/linux-2.6.21.x/include \
-L/home/adeno/fongpio/sdk3301/RT288x_SDK/source/linux-2.6.21.x/lib \
-o mygpio mygpio.c

 そして、mygpio wを実行してみると

おぉ!ちょっと速くなった! 4ms!!
100msとか780msから比べると激速だね!

1つをクロックとして、もう1つをデータとした場合、
下がクロック、上がデータ a(61h)をbit0から送った場合

クロックの立ち下がりで読み込んだとして、84msかかる。
ざっくり16文字×2行の液晶に表示する場合は、2.7秒近くかかることになるのか・・。
データを2ポートにすれば、 1.3秒くらい。
まだ遅いな。

クロックが一定であれば、UARTみたいにできるのだけど、
4msが8msになっている見えるところがある…。
さてどうしたものか

2015年3月10日火曜日

busyboxでttyのボーレートを変更したい

FON2405EのシリアルコンソールttyS1のボーレートを57600から9600に変更したかった。
ググるとsttyを使って〜とあったので、sttyを含めた形でカーネル再構築。

sttyコマンドを使ってボーレートを指定しても変化なかった。

他のプロセスで使用していると変更できないらしい。
シリアルコンソールを一時的に止めてみるかと
inittabを変更
sdk3301/RT288x_SDK/source/venders/Ralink/RT3052/inittab

ここにttyS1::respawn:/bin/shというのがある。
しかしいきなり変えるのも怖いので、
 ttyS1::askfirst:/sbin/getty 9600 ttyS1
とした。
別途gettyを含めて再構築。

結果・・・失敗です。なぜか57600で動きやがる。。。


2015年3月2日月曜日

いろいろな進捗

なかなか最近意識が発散気味なのだけれど、
いくつかやることを絞っていきたいと思う。

1.FONなどRT305x搭載のルーターの使い道
  勢いで入手したものもあり、現在4台。
  何に使うか。
  現状はキャラクタ液晶を接続できるように計画中。
  複数台の並列プログラミングとかもやってみたい

2.7セグLED基板のファーム開発
  I2C部分をまだ作っていない。
  ラズベリーパイに接続するために、この部分を作ろう。
  これは比較的早くできそう。

3.FPGAにオープンCPUコアを入れることをやってみたい

4.しりとりで用意したウィキペディアのデータを
  他の用途でも使う。

5.SDRでなにかやる?
  勢いで、DS-DT305を入手したけど・・・。


こんな感じだ。今月も頑張ろう。