// --------------------------------------------------------------------
//  ATtiny2313モニター  WinAVR版 ATtiny2313
//  ATtiny2313_Pmon090616a.c    SIOとI2C入出力対応7segLED表示可能に成りました
//
//      09/06/03 プログラム開始
//               シリアル入力->LCD表示OK   
//            04 受信割込み OK タイマー1割込みOK
//               LCD表示はOKなので7segLEDのダイナミック点灯にハードを変更してソフトの作成
//               ターミナルからシリアルで数字を送信して表示する
//            09 7segLEDx4 ダイナミック表示OK メインループ内で処理 OK
//            13 6桁表示 PORTB,PORD OK b7:x100000 に使用
//               6桁表示 I2C のためb5,b7をa0,a1に変更OK
//               SIOとI2C入出力対応7segLED表示可能に成りました。
//
// --------------------------------------------------------------------
// ヘッダファイルのインクルード
#define F_CPU 8000000UL                     // 8MHz

#include <avr/io.h>
#include <avr/interrupt.h>

// --------------------------------------------------------------------
// データ・タイプの定義
typedef unsigned char BYTE;                 // unsigned 8bit
typedef unsigned int WORD;                  // unsigned 16bit
typedef signed char bool;                   // unsigned 8bit
typedef int INT;                            // signed 16bit

#define true  1
#define false 0
#define sbi(BYTE,BIT) BYTE |= _BV(BIT)      // BYTEの指定BITに1をセット
#define cbi(BYTE,BIT) BYTE &= ~_BV(BIT)     // BYTEの指定BITをクリア

// --------------------------------------------------------------------
// シリアル
#define UART_BAUD 9600
#define UARTKbHit() (UCSRA & (1<<RXC))      // 受信チェック
#define UARTRead()  UDR                     // 受信データ取得
#define BUF_SIZE 12                         // バッファサイズ
#define PUTCH(x) UARTWrite(x)               // シリアル1文字出力
#define GETC() UARTRead()
#define KBHIT() UARTKbHit()

#define UART_BUFSIZE 12
volatile unsigned char UART_buff[UART_BUFSIZE];
volatile unsigned char UART0Count;
volatile unsigned char usart_recvData;      // USARTで受信したデータ

// --------------------------------------------------------------------
// LCD
#define LCD_CTL_PORT        PORTB
#define LCD_DATA_PORT       PORTB
#define LCD_CTL_PORT_DDR    DDRB
#define LCD_DATA_PORT_DDR   DDRB
#define LCD_D4  0
#define LCD_D5  1
#define LCD_D6  2
#define LCD_D7  3
#define LCD_RS  4
#define LCD_E   5
#define LCD_COL 16

// --------------------------------------------------------------------
// タイマー割込み
#define CYCLES_PER_US ((F_CPU)/1000000)     // cpu cycles per microsecond
#define TCNT1_InitVal   (65536-80)          // 10ms (128usX80=10.24ms)
volatile unsigned int  count1;              // 割込みカウンタ1
BYTE flg =0;

// --------------------------------------------------------------------
// 配列、変数
char buff[BUF_SIZE];
//       0    1    2    3    4    5    6    7    8    9
//BYTE moji[]={0x5f,0x44,0x3E,0x76,0x65,0x73,0x7B,0x46,0x7F,0x77};  
//   PORTB b0:f b1:a b2:b b3:e b4:d b5:g b6:c           7segの割り当て
//   PORTD b2:x1 b3:x10 b4:x100 b5:x1000 b6:x10000      5桁のドライブ 
//----------------------------------------------------------------------
//       0    1    2    3    4    5    6    7    8    9
BYTE moji[]={0x5f,0x44,0x3E,0x76,0x65,0x73,0x7B,0x46,0x7F,0x77};  
//   PORTB b0:f b1:a b2:b b3:e b4:d a0:g b6:c           7segの割り当て
//   PORTD b2:x1 b3:x10 b4:x100 b5:x1000 a0:x10000      6桁のドライブ 



//  Initialize Timer/Counter1
void init_Timer1()
{
    TCCR1B = 0x00;              // Stop Timer/Counter1
    TCNT1  = TCNT1_InitVal;     // Initial Value of Timer/Counter1
//    TIMSK = _BV(TOIE1);           // Timer/Counter1 Overflow Interrupt Enable
    TCCR1B = 0x05;              // Start Timer/Counter1 clk/1024 125nsX1024=128us
    count1 = 0;

//    TIMSK |= _BV(TOIE1);      // Timer/Counter1 Overflow Interrupt Enable
//    TIMSK &= ~_BV(TOIE1);     // Timer/Counter1 Overflow Interrupt Enable

}

//-----------------------------------------------------------------------------
//  Timer/Counter1 Overflow Handler
ISR(TIMER1_OVF_vect)
{
    TCNT1 = TCNT1_InitVal;
    count1++;
/*
    if(count1 < 50) cbi(PORTD,2);   // PD2 debug
    if(count1 > 50) sbi(PORTD,2);
*/
    if(count1 < 50) cbi(PORTA,1);   // PA1 debug
    if(count1 > 50) sbi(PORTA,1);

    if(count1 >= 100){
        flg=1;
        count1=0;
    }
}

// ----------------------------
// USART初期化 ATtiny2313
void UARTInit(void) {
    // ボーレート (U2X = "0")
    UBRRL = (F_CPU / (16UL * UART_BAUD)) - 1;
    UBRRH = 0;
//  UCSRB = (1<<TXEN) | (1<<RXEN);              // tx/rx enable
    UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);     // 送受信許可、受信完了割り込み許可
    UCSRC = (1<<UCSZ1) | (1<<UCSZ0);            // 8bit Stop1 パリティなし
}

//-----------------------------------------------------------------------------
/** UART受信割り込み **/
ISR(USART_RX_vect){

    if(bit_is_clear(UCSRA,FE)){     // フレーミングエラー
        usart_recvData = UDR;       // 受信データ
        UART_buff[UART0Count] = usart_recvData;
        UART0Count++;
        if(UART0Count == UART_BUFSIZE) UART0Count = 0;    // buffer overflow
    }
}

// ----------------------------
// USART 1バイトシリアル送信
void UARTWrite(BYTE dat) {
    
    while ( !( UCSRA & (1<<UDRE)) ) {}; // 送信バッファが空になるまで待つ
    UDR = dat;                          // 送信データをセット
}

// ---------------------------------
// シリアルへ文字列を送信
void UARTstr(){
    BYTE i;

    for(i=0; i<BUF_SIZE; i++){
        if(buff[i]==0){
            break;
        } else {
            PUTCH(buff[i]);
        }
    }
    PUTCH('\r');
    PUTCH('\n');
}

// ---------------------------------
// シリアルへ文字列を送信
static void send_text(char *text)
{
//char ch;
    while(*text)
    {
      UARTWrite(*text++);
    }
}

//----------------------------------
// 遅延タイマー
void delay_us(uint32_t time_us) 
{
    uint32_t delay_loops;
    register uint32_t i;

    delay_loops = (time_us+3)/5*CYCLES_PER_US;      // +3 for rounding up (dirty) 

    // one loop takes 5 cpu cycles 
    for (i=0; i < delay_loops; i++) {
        asm volatile ("nop\n");
    };
}

//----------------------------------------------------------
// LCD EnableフラグをON, OFF
void lcd_e(void)
{
    LCD_CTL_PORT |= _BV(LCD_E);
    LCD_CTL_PORT &= ~_BV(LCD_E);
}

//----------------------------------------------------------
// LCDにコマンド(RS=0)を送信。
void lcd_cmdout_s(char cmd)
{
    LCD_CTL_PORT &= ~_BV(LCD_RS);
    LCD_DATA_PORT = (LCD_DATA_PORT & 0xF0) | cmd;
    lcd_e();
}

//----------------------------------------------------------
// LCDにコマンドを送信
void lcd_putcmd(char cmd)
{
    lcd_cmdout_s(cmd >> 4);
    lcd_cmdout_s(cmd & 0x0F);
    delay_us(10000);
}

//----------------------------------------------------------
// LCDにデータ(RS=1)送信
void lcd_dataout_s(char ch)
{
    LCD_CTL_PORT |= _BV(LCD_RS);
    LCD_DATA_PORT = (LCD_DATA_PORT & 0xF0) | ch;
    lcd_e();
}

//----------------------------------------------------------
//  LCDに文字を表示
void lcd_putch(char ch)
{
    lcd_dataout_s(ch >> 4);
    lcd_dataout_s(ch & 0x0F);
    delay_us(4);
}

//----------------------------------------------------------
//  LCDに文字列を表示
void lcd_putstr(char *str)
{
    while(*str != '\0')
    {       
        lcd_putch(*str);
        str++;
    }
}

//----------------------------------------------------------
// LCDの表示クリア
void lcd_cls(void)
{
  lcd_putcmd(0x01);
}

//----------------------------------------------------------
// 引数で指定した場所にLCDのカーソルを移動します。
// lineは0基準となります。
// 1行目の1文字目をさす場合には、lcd_gotopos(1,0);と指定します。
void lcd_gotopos(char line, char column)
{
    if (line == 0)
    {
        lcd_putcmd(0x80 + column);
    }else{
        lcd_putcmd(0xC0 + column); 
    }
}

//----------------------------------------------------------
// LCDの初期化処理
void lcd_init(void)
{
    delay_us(16000);
    LCD_CTL_PORT &= (~_BV(LCD_RS) | ~_BV(LCD_E));

    //初期化  
    lcd_cmdout_s(0x03); delay_us(5000);
    lcd_cmdout_s(0x03); delay_us(120);
    lcd_cmdout_s(0x03); delay_us(120);
    lcd_cmdout_s(0x02); delay_us(120);
    lcd_putcmd(0x28);
    lcd_putcmd(0x08);
    lcd_putcmd(0x01);
    lcd_putcmd(0x0C);
}

// ---------------------
// 数値->文字列変換
void TempToStr(WORD val) {
    BYTE d;

    if(val & 0x8000) {
        buff[0] = '-';                  // 負数のとき
        val &= ~0x8000;                 // MSBをクリア
    } else {
        //buff[0] = '+';                // 正数のとき
        buff[0] = ' ';
    }

    d = val / 1000;                     // 1000の桁
    if(d!=0){
        buff[1] = (val / 1000) + '0';   // 1000の桁
    }else{
        buff[1] = ' ';                  // 1000の桁
    }
    val = val % 1000;
    buff[2] = (val / 100) + '0';        // 100の桁

    val = val % 100;
    buff[3] = (val / 10) + '0';         // 10の桁

    buff[4] = '.';                      // 少数点
    buff[5] = val % 10 + '0';           // 1の桁
    buff[6] = '\0';                     // デリミタ
}

// ------------------------------------------------
// MAIN
// ------------------------------------------------
int main(void) {
    BYTE ch;
    int i=0;
    int j=0;

    PORTA = 0x00;
    DDRA  = 0x0F;           // 7segLED a0:5桁ドライブ a1表示データg 
    DDRB  = 0xFF;           // 7segLED 表示データ
    PORTB = 0x00;
    DDRD  = 0xFE;           // 5桁ドライブ
    PORTD = 0x00;

    UARTInit();                                 // 非同期シリアル通信の初期化
    init_Timer1();                              // Initialize Timer/Counter1
/*
    lcd_init();
    lcd_cls();
    lcd_putstr("ATtiny2313 Pmon");
    lcd_gotopos(1,0);
    lcd_putstr("2009/06/04 puw2");

    delay_us(500000);
    lcd_cls();
*/
    sei();                                      // 全割り込み許可

    send_text("ATtiny2313 Pmon 7segLED 090616a\n\r");

    // メイン・ループ
    while(1) {
        ch=usart_recvData;  // 受信データ
        if(ch){                             
            switch(ch) {                        // コマンド解析処理(1文字ずつ渡す)
                case '\b':                      // バックスペース
                    if(UART0Count > 0) {
                        UART0Count--;           // バッファから1文字削除
                        PUTCH('\b');            // ターミナルの1文字を消去
                        PUTCH(' ');
                        PUTCH('\b');

                        i--;
                        lcd_gotopos(j,i);       // 表示位置設定
                        lcd_putch(' ');         // LCD1文字表示

                    }
                    break;

                case '\n':                      // 読み捨て
                    break;

                case '\r':                      // Enter(コマンド確定)

                    //cli();                    // 全割り込み禁止
                    UART_buff[UART0Count] = '\0';   // NULLコード
                    PUTCH('\r');                // 改行
                    PUTCH('\n');

                    ch = UART_buff[0];          // 1文字目
                    //ch = toupper(UART_buff[0]);// 1文字目を大文字に変換
                    switch(ch) {
                        case ('I'):
                            TIMSK |= _BV(TOIE1);// Timer1 Interrupt Enable
                            flg=1;
                            break;

                        case ('i'):
                            TIMSK &= ~_BV(TOIE1);// Timer1 Interrupt DisEnable
                            flg=0;
                            break;

                        default:
                            PUTCH('?');     // '?'
                            PUTCH('\r');    // 改行
                            PUTCH('\n');
                            break;
                    
                    } // switch
                UART0Count = 0;             // コマンドバッファをクリア
                break;

            default:
                PUTCH(ch);                  // シリアル エコーバック
/*
                lcd_putch(ch);              // LCD1文字表示
                i++;
                if (i>=16){                 // カーソル位置の変更16文字
                    i=0;
                    j++;
                    lcd_gotopos(j,i);
                }
                if(j>1){                    // カーソル位置の変更改行
                    j=0;
                    lcd_gotopos(j,i);
                }
*/
                break;
            } // switch

        usart_recvData = '\0';
        } // jushin loop

        if(flg==1){
            send_text("hello\n\r");         // タイマー1割込みでシリアルへ文字列送信    
            flg=0;
        }

        j= UART_buff[i] & 0x0F;             // メインループで7segLED表示処理
        PORTB = moji[j];

        if(j>1){sbi(PORTA,1);}else{cbi(PORTA,1);}   // 7segLED g
        if(j==7)cbi(PORTA,1);                       // 7segLED g

        PORTD = 1 << (i+2);
        if(i==5){sbi(PORTA,0);}else{cbi(PORTA,0);}  // 6桁目

        i++;
        if(i>5) i=0;                                // 6桁表示

        delay_us(1000);
    } // main loop while
}