NEKO BENTO のソースコード [DIY電気]
過去記事でも3回ご紹介してる「猫用自動給餌器をリモコン制御に改造」の続編
「PetSafe おるすばんフィーダー デジタル2食分 バージョン2」という製品を改造しての改造シリーズ第4弾はプログラムソースコードの公開となる
PICマイコンを組み込んでおり、C言語のソースコードである
Summary:
犬猫の餌を入れておいて希望する時間後に蓋が開く商品であるが、ゆういちの希望する仕様ではなかったので改造した
マイコンは割り込みルーチンで時刻を持つ、また家電リモコンからの毎日時刻校正コマンドを受信するため時刻は正確である
左蓋、右蓋のOPEN時刻をセット可能、家電リモコン(スマホから操作)でOPENすることも可能
一か月以上もRUNして問題ないためバグは究極にゼロに近いと思っている
外観はこんな商品
シンプルはゆういちの好みである
左右の蓋が開いた状態
後ろには、黄色いスイッチ2つとAC/電池切り替えスイッチ増設
手に持っているのがオリジナル基板
取付済みが自作基板
自作基板は、Fusion PCBにガーバーデーターを送って製作した
MicrochipのPIC16F88マイコン搭載である
表示器は小型のOLEDを採用している
PICマイコンが持つフォントデーターは234バイト、文字は[0]~[9], [A]~[Z], [:],[?],[-]、最低限のフォントしか持たない
その自作基板の回路図はEagleで設計
プリント基板設計もEagle
シルク印刷で示すJP1,JP3はOLEDの表示モジュールのマウントパターン
基板表裏どちらでもマウントできるようにしている
1980年代制御プログラムをコーディングしていた、
当時勤めていた会社の上司からプログラミングに対してコメントを受けることもあった
上司はアランドロンだった
ゆういちが上司はアランドロンと似ていると言い出したのではない、誰かが言ったので上司とアランドロンはヒモ付いただけである
アランドロンはゆういちに教えた、メインプログラムはグルグル回してタイマーで判断して複数ある処理プロセスに飛ばせばマルチタスクが実現する・ボンジュールと・・・
アランドロンから伝授した手法はゆういちが作り続けてきた多くのソフトに利用させていただいた
ゆういちのメインルーチンは必ず
while(1){ } である
特にこの手法を必要としないソフトも、while(1){ }、まずはこの手法を利用する
なぜならタスクを増やしたい場合に柔軟に対応できるからだ
このソフトもその手法を少しだけ利用させていただいている
NEKO_BENTOメインプログラム For CCSC Compiler
赤外線受信サブルーチン For CCSC Compiler
ありがとうございますm(__)m For アランドロン
ゆういちの上司はアランドロンの髪型と似てるだけじゃなかったけ?
おわり
過去の記事:
猫用自動給餌器をリモコン制御に改造
猫用自動給餌器をリモコン制御に改造、その2
猫用自動給餌器をリモコン制御に改造、その3
人気ブログランキング
「PetSafe おるすばんフィーダー デジタル2食分 バージョン2」という製品を改造しての改造シリーズ第4弾はプログラムソースコードの公開となる
PICマイコンを組み込んでおり、C言語のソースコードである
Summary:
犬猫の餌を入れておいて希望する時間後に蓋が開く商品であるが、ゆういちの希望する仕様ではなかったので改造した
マイコンは割り込みルーチンで時刻を持つ、また家電リモコンからの毎日時刻校正コマンドを受信するため時刻は正確である
左蓋、右蓋のOPEN時刻をセット可能、家電リモコン(スマホから操作)でOPENすることも可能
一か月以上もRUNして問題ないためバグは究極にゼロに近いと思っている
外観はこんな商品
シンプルはゆういちの好みである
左右の蓋が開いた状態
後ろには、黄色いスイッチ2つとAC/電池切り替えスイッチ増設
手に持っているのがオリジナル基板
取付済みが自作基板
自作基板は、Fusion PCBにガーバーデーターを送って製作した
MicrochipのPIC16F88マイコン搭載である
表示器は小型のOLEDを採用している
PICマイコンが持つフォントデーターは234バイト、文字は[0]~[9], [A]~[Z], [:],[?],[-]、最低限のフォントしか持たない
その自作基板の回路図はEagleで設計
プリント基板設計もEagle
シルク印刷で示すJP1,JP3はOLEDの表示モジュールのマウントパターン
基板表裏どちらでもマウントできるようにしている
1980年代制御プログラムをコーディングしていた、
当時勤めていた会社の上司からプログラミングに対してコメントを受けることもあった
上司はアランドロンだった
ゆういちが上司はアランドロンと似ていると言い出したのではない、誰かが言ったので上司とアランドロンはヒモ付いただけである
アランドロンはゆういちに教えた、メインプログラムはグルグル回してタイマーで判断して複数ある処理プロセスに飛ばせばマルチタスクが実現する・ボンジュールと・・・
アランドロンから伝授した手法はゆういちが作り続けてきた多くのソフトに利用させていただいた
ゆういちのメインルーチンは必ず
while(1){ } である
特にこの手法を必要としないソフトも、while(1){ }、まずはこの手法を利用する
なぜならタスクを増やしたい場合に柔軟に対応できるからだ
このソフトもその手法を少しだけ利用させていただいている
NEKO_BENTOメインプログラム For CCSC Compiler
/**********************************************
TITLE: NEKO BENTO
FILENAME: neko_bento.c
TARGET DEVICE: PIC16F88, 10MHz X'tal
DATE: 2019.05.11 - 2023.12.14
VERSION 00 2019.08.04 OLED表示機能
VERSION 01 2019.09.19 FOR NEW PCB(Fusion PCB)
VERSION 02 2019 11.02 TIME CLOCK ADJUST
VERSION 03 2022 01.02 起動時黄色ボタン押していたら時刻入力をスキップ
AUTHOR: Yuichi
***********************************************/
#include <16f88.h>
#use delay(clock=10000000)
#FUSES HS,NOWDT,NOPROTECT,PUT,NOMCLR,NOBROWNOUT,NOLVP
#use i2c(master,sda=PIN_B1,scl=PIN_B4,FAST)
#ignore_warnings 203
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#define TIMEADJ 187 //LARGE NUMBERS=FAST CLOCK SMALL NUMBERS:CLOCK is Delayed
//大きい数字は時計が進む、小さい数字は時計が遅れる
#define EEPROM_SAVED 0xcc
// OKNG
#define OK 0
#define NG 1
//24Hを超える(単位は分)、60*24-1=1439 以上は無効とするため
//1440以上であればいいが、切りのいい1500としている
#define INVALID_TIME 1500
// AポートのI/Oビット配置
// 上からLSBで割り当てればよい
// plビットをHIGHにするには PORTA.pl=1 とすればいい
struct porta_pin_map1 { // This structure is overlayed
BOOLEAN Infrared_rx;
BOOLEAN pushsw;
BOOLEAN left;
BOOLEAN right;
BOOLEAN pl; //pilot lamp
} PORTA;
struct porta_pin_map2 { // This structure is overlayed
BOOLEAN reserve2;
BOOLEAN sda;
BOOLEAN reserve3;
BOOLEAN reserve4;
BOOLEAN scl;
BOOLEAN reserve5;
} PORTB;
#byte PORTA = 0x05
#byte PORTB = 0x06
struct font_bit{
BOOLEAN b0;
BOOLEAN b1;
BOOLEAN b2;
BOOLEAN b3;
BOOLEAN b4;
BOOLEAN b5;
BOOLEAN b6;
BOOLEAN b7;
};
#include "hinagata.h" // 型宣言
#include "Infrared_rx.h" // Infrared Rmote cont
//割り込みでカウントアップする変数
int16 g_209ms_count = 0;
// get_4_number()で返される値
struct g_four_digit{ //リモコンボタン4桁で時間入力
int16 data; //分
BOOLEAN timeout; //timeoutしたらTRUE
} g_four_digit;
// 蓋を開ける時間 0時0分から何分後に蓋を開けるかの設定
// 0-1439 は有効
// INVALID_TIME は無効
int16 g_cover[2];
// Clock
// このプログラムでは時と分をそれぞれカウントせず、分だけカウントしている
// 0~1439分で24時間
int16 g_minutes=0; // 分カウント
// cover open flag
BOOLEAN g_open[2] = {FALSE,FALSE}; //蓋が開いたら一分間だけTRUEになる
// OLED光ってるSTATUS
BOOLEAN g_oled = FALSE;
// OLEDをSLEEPにするための時間計測
int16 g_oled_cnt = 0;
// 6*8 bitmap font
// Alphaは大文字のみ
BYTE const font[39][6]={
{0x7C,0xFE,0x82,0xFE,0x7C,0x00}, //0
{0x00,0x04,0xFE,0xFE,0x00,0x00}, //1
{0xE4,0xF2,0xB2,0xBE,0x9C,0x00}, //2
{0x44,0xC6,0x92,0xFE,0x6C,0x00}, //3
{0x1C,0x12,0xFE,0xFE,0x10,0x00}, //4
{0x4E,0xCE,0x8A,0xFA,0x72,0x00}, //5
{0x7C,0xFE,0x8A,0xFA,0x70,0x00}, //6
{0x00,0x02,0xF2,0xFE,0x0E,0x00}, //7
{0x6C,0x9E,0xBA,0xF2,0x6C,0x00}, //8
{0x1C,0xBE,0xA2,0xFE,0x7C,0x00}, //9
{0xC0,0x72,0x4E,0x7E,0xF8,0xE0}, //A 10
{0xFE,0xFE,0x92,0x92,0xFE,0x6C}, //B
{0x7C,0xFE,0x82,0x82,0xEE,0x6C}, //C
{0xFE,0xFE,0x82,0xC6,0x7C,0x38}, //D
{0xFE,0xFE,0x92,0x92,0x92,0x00}, //E
{0xFE,0xFE,0x12,0x12,0x12,0x00}, //F
{0x7C,0xFE,0x82,0xD2,0x76,0xF4}, //G
{0xFE,0xFE,0x10,0x10,0xFE,0xFE}, //H
{0x00,0x00,0xFE,0xFE,0x00,0x00}, //I
{0x60,0xE0,0x82,0xFE,0x7E,0x00}, //J
{0xFE,0xFE,0x38,0xF4,0xE2,0x00}, //K 20
{0xFE,0xFE,0x80,0x80,0xC0,0x00}, //L
{0xFE,0x1E,0x78,0xE0,0x18,0xFE}, //M
{0xFE,0x1C,0x38,0x70,0xFE,0x00}, //N
{0x7C,0xFE,0x82,0x82,0xFE,0x7C}, //O
{0xFE,0xFE,0x22,0x22,0x3E,0x1C}, //P
{0x3C,0x7E,0x42,0xE2,0xFE,0xBC}, //Q
{0xFE,0xFE,0x12,0x32,0xFE,0xDC}, //R
{0x4C,0x9E,0xBA,0xF2,0x64,0x00}, //S
{0x02,0x02,0xFE,0xFE,0x02,0x02}, //T
{0x7E,0xFE,0xC0,0x80,0x7E,0x00}, //U 30
{0x0E,0x3E,0xF0,0xC0,0x70,0x0E}, //V
{0x1E,0xFE,0xE0,0x7C,0xE0,0x3E}, //W
{0x8E,0x7E,0x38,0xFC,0xE2,0x00}, //X
{0x06,0x1E,0xF8,0xF0,0x18,0x06}, //Y
{0x82,0xE2,0xFA,0xBE,0x8E,0x82}, //Z
{0x00,0x00,0x24,0x24,0x00,0x00}, //:
{0x0C,0x0E,0xA2,0xB2,0x1E,0x0C}, //?
{0x00,0x10,0x10,0x10,0x10,0x00}, //-
};
// OLED PRINT POSITION
int oled_x=0;
int oled_y=0;
/*---------------------------------------------
Timer interrupt interval 209ms
0.4us*8*(65535-7)=209ms
---------------------------------------------*/
#INT_TIMER1
void watch_handler( void )
{
// set_timer1(0)だとフルカウントする。0->FFFF->0 で割り込み発生
// set_timer1(12)だと12少ないカウント、12->FFFF->0 で割り込み発生
set_timer1( TIMEADJ ); // 65535-11
++g_209ms_count; // 209msごとにカウントアップ
if(g_209ms_count > 286 ){ // 209ms*286=60sec
g_209ms_count = 0;
g_open[0]=g_open[1]=FALSE; // Cover open flag clear
++g_minutes;
if( g_minutes > 1439 ){ // 24Hour
g_minutes = 0;
}
}
}
/*---------------------------------------------
main module
---------------------------------------------*/
void main( void ){
int32 scode;
char title[20];
BOOLEAN select_time = 0;
sprintf( title , "\fNEKO\nBENTO %s", "03"); //VERSION HERE
set_tris_a( 0x03 ); // BIT0(IR) BIT1(SW) IS IN
set_tris_b( 0x12 ); // SDA, SCL is INPUT
PORTA = 0;
PORTB = 0;
if( read_eeprom( 0 ) != EEPROM_SAVED ){ // Never save
g_cover[0] = INVALID_TIME; //Left
g_cover[1] = INVALID_TIME; //Right
}else{
g_cover[0] = make16( read_eeprom(2), read_eeprom(1) );
g_cover[1] = make16( read_eeprom(4), read_eeprom(3) );
}
delay_ms(300);
oled_init();
oled_resume( TRUE ); //NO SLEEP
printf( oled_putc, "%s", title );
delay_ms( 1000 );
// If not Push Yellow Double Switchs
if( PORTA.pushsw != 0 ){
printf( oled_putc, "\f SET TIME\n ? " );
sets_clock();
}
//delay_ms( 1000 );
printf( oled_putc, "\f START");
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 ); // 8bit max 3.2us
PORTA.pl = 1; //START NOTICE
set_timer1( TIMEADJ );
enable_interrupts( INT_TIMER1 );
enable_interrupts( GLOBAL );
while(1){
// このループは約2msぐらい
scode = Infrared_read();
++g_oled_cnt; //時間計測
// IOのアサート
if( scode != 0 ){
if( scode == PS2_L1 ){ // LEFT COVER [L1]
oled_resume( TRUE );
printf( oled_putc, "\f L OPEN" );
open_bento( 0 );
}else if( scode == PS2_R1 ){ // RIGHT COVER [R1]
oled_resume( TRUE );
printf( oled_putc, "\f R OPEN" );
open_bento( 1 );
}else if ( scode == PS2_ENTER ){ // 両方開ける[ENTER]
oled_resume( TRUE );
printf( oled_putc, "\fALL OPEN" );
open_bento( 0 );
open_bento( 1 );
}else if( scode == PS2_L3 ){ // set left time [L3]
oled_resume( TRUE );
printf( oled_putc, "\f L OPEN\n ? " );
get_4_number();
if( g_four_digit.timeout == FALSE ){
g_cover[0] = g_four_digit.data;
save_data();
printf( oled_putc, "\f LEFT\n SAVED");
}
}else if( scode == PS2_R3 ){ // set right time [R3]
oled_resume( TRUE );
printf( oled_putc, "\f R OPEN\n ? " );
get_4_number();
if( g_four_digit.timeout == FALSE ){
g_cover[1] = g_four_digit.data;
save_data();
printf( oled_putc, "\f RIGHT\n SAVED");
}
}else if( scode == PS2_TIME ){ // セットしたLR蓋が開く時刻表示と現在時刻表示
oled_resume( TRUE );
select_time ^= 1;
if( select_time == 1 ){
printf( oled_putc, "\f L " );
if( g_cover[0] == INVALID_TIME ){
printf( oled_putc, "--:--\n" );
}else{
printf( oled_putc, "%02ld:%02ld\n", g_cover[0]/60, g_cover[0]%60 );
}
printf( oled_putc, " R " );
if( g_cover[1] == INVALID_TIME ){
printf( oled_putc, "--:--" );
}else{
printf( oled_putc, "%02ld:%02ld", g_cover[1]/60, g_cover[1]%60 );
}
}else{
printf( oled_putc, "\fCUR TIME\n %02ld:%02ld", g_minutes/60 , g_minutes%60 );
}
}else if( scode == PS2_AUDIO ){ // 現在時刻を設定する [2] [0] [5] [2] -> 20時52分
oled_resume( TRUE );
printf( oled_putc, "\f SET TIME\n ? " );
sets_clock();
printf( oled_putc, "\f START");
}else if( scode == PS2_PROGRAM ){ // Time Adjust 3時00分にセットする
// スマート家電リモコンから定期的にコマンドが来る
g_209ms_count=47;
g_minutes = 180;
}else if( scode == PS2_TITLE ){ // DISPLAY VERSION
oled_resume( TRUE );
printf( oled_putc, "%s", title );
}
delay_ms( 300 );
}//if
// TimerでLEFT蓋を開ける
if( g_minutes == g_cover[0] && g_open[0] == FALSE ){
open_bento( 0 );
g_open[0] = TRUE; //蓋が開いているフラグは一分後にタイマー割り込みでリセットされる
}
// TimerでRIGHT蓋を開ける
if( g_minutes == g_cover[1] && g_open[1] == FALSE ){
open_bento( 1 );
g_open[1] = TRUE;
}
// リモコンが押されなくなって一定時間経過したら
if( g_oled_cnt > 8000 ){
oled_resume( FALSE ); // SLEEP MODE
}
if( PORTA.pushsw == 0 ){
oled_resume( TRUE );
printf( oled_putc, "\fALL OPEN" );
open_bento( 0 );
open_bento( 1 );
delay_ms( 3000 );
}
}//while
}
/*---------------------------------------------
SAVE DATA
---------------------------------------------*/
void save_data( void )
{
disable_interrupts( INT_TIMER1 );
disable_interrupts( GLOBAL );
write_eeprom(0,EEPROM_SAVED );
write_eeprom(1,g_cover[0] & 0xff );
write_eeprom(2,(g_cover[0] >> 8) & 0xff );
write_eeprom(3,g_cover[1] & 0xff );
write_eeprom(4,(g_cover[1] >> 8) & 0xff );
delay_ms(10);
enable_interrupts( INT_TIMER1 );
enable_interrupts( GLOBAL );
}
/*---------------------------------------------
MANUAL SET CLOCK
---------------------------------------------*/
void sets_clock( void )
{
get_4_number();
if( g_four_digit.timeout == FALSE && g_four_digit.data != INVALID_TIME){
g_minutes = g_four_digit.data;
g_209ms_count = 0;
}
}
/*---------------------------------------------
OPEN BENTO COVER
---------------------------------------------*/
void open_bento( BOOLEAN flag )
{
if( flag == 0 ){
PORTA.left = 1;
}else{
PORTA.right = 1;
}
delay_ms( 300 ); // wait
PORTA.left = 0;
PORTA.right = 0;
delay_ms( 600 );
}
/*---------------------------------------------
Input 4 botton
現在時刻設定、蓋を開ける時刻設定で使用される
From PS2 [0]-[9]
Return 0-1439, INVALID_TIME
タイムアウトしたフラグで返す
--------------------------------------------*/
void get_4_number( void )
{
long loop_count=0; //タイムアウト計測用
int32 scode; //リモコンコード
int digit=0; //桁ポインタ
long data[5]; //桁ごとの分データ
long num; //数字ボタンに対応する数値が入る
int lutable[11] = {1,2,3,4,5,6,7,8,9,0}; //scodeの4LSBでこのLUTを見る
long minutes_data[5] ={600,60,10,1}; //Digitごとの単位
delay_ms( 300 );
printf( oled_putc, "----" ); //数字が入力されていない状態表示
oled_x -= 4; //表示位置を4文字分戻す
PORTA.pl = 0;
while(1){
++loop_count; //この関数のTimeout計測
scode = Infrared_read();
if( scode != 0 ){
if( scode >= PS2_1 && scode <= PS2_0 ){
num = lutable[scode & 0x0f]; // covert to num
if( num <= digit_max(digit,num) ){ //入力数値の制限
data[digit] = ( num * minutes_data[digit] );
printf( oled_putc, "%ld",num );
++digit; // next digit
delay_ms( 300 );
}
}
if( scode == PS2_LEFT ){ //BACK SPACE
if( digit != 0 ){ // digit 0 以外で動作するように
--oled_x;
printf( oled_putc, "-" );
--oled_x;
--digit;
delay_ms( 300 );
}
}
// TIMER CANCEL
// タイマーでNEKO弁当は開けない
if( scode == PS2_L3 || scode == PS2_R3 ){
delay_ms( 300 );
g_four_digit.timeout = FALSE;
g_four_digit.data = INVALID_TIME; //INVALID_TIME is Invalid
PORTA.pl = 1;
return;
}
loop_count=0;
}
// Sets Success
// 4桁入力されたらこの関数を終える
if( digit == 4 ){
g_four_digit.timeout = FALSE;
g_four_digit.data = data[0]+data[1]+data[2]+data[3]; //最終データ、分データ
PORTA.pl = 1;
return;
}
// PL blink while RUN this function
if( loop_count > 77 ){
PORTA.pl ^= 1;
loop_count = 0;
}
#if 0
// time out 10sec
if( loop_count > 2307 ){ // 2307 maybe 6s
g_four_digit.timeout = TRUE;
return;
}
#endif
}
}
/*---------------------------------------------
Digit MAX
時刻を4桁入力する際にそれぞれの桁の最大値を返す
24:00が最大なので、最初の桁は最大が[2]である
最初の桁に[2]を入力した場合は次の桁の最大とは[3]となる
最初の桁に[1]を入力した場合は次の桁の最大とは[9]となる
---------------------------------------------*/
int digit_max( int digit, int num )
{
int max;
static int digit0num;
switch( digit ){
case 0:
max = 2;
digit0num = num;
break;
case 1:
max = (digit0num == 2) ? 3 : 9; // digit0によって最大値が変わる
break;
case 2:
max = 5; //Minutesの10の桁は最大5
break;
case 3:
max = 9; //Minutesの1の桁は最大9
break;
default:
break;
}
return( max );
}
/*---------------------------------------------
文字列表示する
CCSCの特殊な使い方をする printf( vfd_putc, *fmt, hikisuu )
というようにprintfで使う
data; 文字
---------------------------------------------*/
void oled_putc( char c )
{
int array;
if( c == '\n' ){ //改行
oled_x = 0;
oled_y = 1;
return;
}
if( c == '\f' ){ //画面クリア
oled_clr(0);
return;
}
if( isdigit(c) ){
array = c - '0'; //数字だったら(0-9に変換)
}else if( isalpha(c) ){
array = toupper(c) - 55;
}else if( c == ':'){
array = 36;
}else if( c == '?' ){
array = 37;
}else if( c == '-' ){
array = 38;
}else{
array = 50; // Invalid char is SPACE
}
char1216( array, oled_x, oled_y );
++oled_x; //次回のためにx表示位置変更
if( oled_x > 8 ){
oled_x = 0; //次回のためにx表示位置変更
oled_y ^= 1; //次回のためにy表示位置変更
//yは0と1のみなので反転演算
}
}
/* ----------------------------------------------------------------
OLEDの初期化
---------------------------------------------------------------- */
void oled_init(void)
{
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x8d); // disable charge pump
i2c_write(0x10);
delay_ms(1);
i2c_write(0xa8); //SET MUX RATIO
i2c_write(0x3f);
i2c_write(0xd3); //SET DISPLAY OFFSET
i2c_write(0x00);
i2c_write(0x40); //SET DISPLAY START LINE
#if 0
i2c_write(0xa0); //SET SEGMENT RE-MAP
i2c_write(0xc0); //SET COM OUTPUT SCAN DIRECTION
i2c_write(0xda); //SET COM PINS HARDWARE CONFIGURATION
i2c_write(0x02);
#else
i2c_write(0xa1); //SET SEGMENT RE-MAP
i2c_write(0xc8); //SET COM OUTPUT SCAN DIRECTION
i2c_write(0xda); //SET COM PINS HARDWARE CONFIGURATION
i2c_write(0x22);
#endif
i2c_write(0x81); //SET CONTRAST CONTROL
i2c_write(0x3f); // MAX 7F
i2c_write(0xa4); //DISABLE ENTRE DISPLAY ON
i2c_write(0xa6); //SET NORMAL DISPLAY
i2c_write(0xd5); //SET OSC FREQUENCY
i2c_write(0x80);
i2c_write(0x8d); //ENSBLE CHSRGE PUMP REGULATOR
i2c_write(0x14);
i2c_write(0xaf); //DISPLAY ON
i2c_stop();
}
/* ----------------------------------------------------------------
OLEDの画面クリア
data : 埋めるデータ
---------------------------------------------------------------- */
void oled_clr(int data) // OLED 画面消去
{
int32 i;
oled_x = oled_y = 0;
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x20); // Set memory addressing mode
i2c_write(0x00); // Horizontal addressing mode
i2c_write(0x21); // Set column address
i2c_write(0x00); // Column start address 0
i2c_write(0x7F); // Column end address 127d
i2c_write(0x22); // Set page address
i2c_write(0x00); // Page start address 0
i2c_write(0x03); // Page end address 3d
i2c_stop();
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x40);
for(i=0; i<512; i++){ // 128COL * 4page
i2c_write(data); // Out data
}
i2c_stop();
}
/* ----------------------------------------------------------------
OLED resume
flag:
true is NORMAL MODE
false is SLEEP
---------------------------------------------------------------- */
void oled_resume( int flag)
{
if( flag != g_oled ){
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
if( flag == false ){
i2c_write( 0xae ); // SLEEP
}else{
i2c_write( 0xaf ); // DISPLAY ON
}
i2c_stop();
g_oled = flag;
}
g_oled_cnt = 0;
}
/* ----------------------------------------------------------------
OLED SETS PRINT POSITION
---------------------------------------------------------------- */
void oled_gotoxy( int x, int y)
{
oled_x = x;
oled_y = y;
}
/* ----------------------------------------------------------------
OLEDに一文字出力
font_code:
配列を指定(0~)
cx:
出力水平位置(0~8)
cy:
出力垂直位置(0~1)
---------------------------------------------------------------- */
void char1216( int font_code, int cx, int cy ) // 12x16dot FONT
{
int x;
struct font_bit FONT_BIT1; //before 8bit font
struct font_bit FONT_BIT2; //after extent 8bit font
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x20); // Set memory addressing mode
i2c_write(0x00); // Horizontal addressing mode
i2c_write(0x22); // Set Page Address
i2c_write(cy*2); // Page Start
i2c_write(cy*2+1); // Page End
i2c_write(0x21); // Set Column Address
i2c_write(cx*13); // Start Address(0-9)
i2c_write(cx*13+11); // End Address
i2c_stop();
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x40);
// 6*8 FONTを2倍にする処理、粗い文字となるがFONTデータが少量ですむ
// MSBはOLED画面下方向
// 1st page
// FONT_BIT1.b0 -> FONT_BIT2_b0
// FONT_BIT1.b0 -> FONT_BIT2_b1
// FONT_BIT1.b1 -> FONT_BIT2_b2
// FONT_BIT1.b1 -> FONT_BIT2_b3
// FONT_BIT1.b2 -> FONT_BIT2_b4
// FONT_BIT1.b2 -> FONT_BIT2_b5
// FONT_BIT1.b3 -> FONT_BIT2_b6
// FONT_BIT1.b3 -> FONT_BIT2_b7
// next page
// FONT_BIT1.b4 -> FONT_BIT2_b0
// FONT_BIT1.b4 -> FONT_BIT2_b1
// FONT_BIT1.b5 -> FONT_BIT2_b2
// FONT_BIT1.b5 -> FONT_BIT2_b3
// FONT_BIT1.b6 -> FONT_BIT2_b4
// FONT_BIT1.b6 -> FONT_BIT2_b5
// FONT_BIT1.b7 -> FONT_BIT2_b6
// FONT_BIT1.b7 -> FONT_BIT2_b7
if( font_code > 49 ){ // SPACE(BLANK) Draw
for( x=0 ; x<24 ; ++x ){
i2c_write( 0 );
}
}else{
for( x=0 ; x<6 ; ++x ){
FONT_BIT1 = font[font_code][x];
//FONT_BIT1からFONT_BIT2へBITを振り分ける
FONT_BIT2.b0 = FONT_BIT2.b1 = FONT_BIT1.b0;
FONT_BIT2.b2 = FONT_BIT2.b3 = FONT_BIT1.b1;
FONT_BIT2.b4 = FONT_BIT2.b5 = FONT_BIT1.b2;
FONT_BIT2.b6 = FONT_BIT2.b7 = FONT_BIT1.b3;
i2c_write( (int)FONT_BIT2 ); //横方向に同FONTを2 DOT
i2c_write( (int)FONT_BIT2 ); //これで2 DOT分
}
for( x=0 ; x<6 ; ++x ){
FONT_BIT1 = font[font_code][x];
//FONT_BIT1からFONT_BIT2へBITを振り分ける
FONT_BIT2.b0 = FONT_BIT2.b1 = FONT_BIT1.b4;
FONT_BIT2.b2 = FONT_BIT2.b3 = FONT_BIT1.b5;
FONT_BIT2.b4 = FONT_BIT2.b5 = FONT_BIT1.b6;
FONT_BIT2.b6 = FONT_BIT2.b7 = FONT_BIT1.b7;
i2c_write( (int)FONT_BIT2 );
i2c_write( (int)FONT_BIT2 );
}
}
i2c_stop();
}
//EOF
赤外線受信サブルーチン For CCSC Compiler
////////////////////////////////////////////////////////////////
//
// Infrared_X.H
// 赤外線リモコン受信プログラム for ソニー
//
// AUTHOR: Yuichi
//
// NOTE:
// (1)構造体の宣言例
// struct porta_pin_map { // This structure is overlayed
// BOOLEAN unused1;
// BOOLEAN Infrared_rx;
// BOOLEAN sw;
// BOOLEAN beep;
// BOOLEAN Infrared_tx;
// int unused2 : 3;
// }PORTA;
// (2)1つのタイマーを使用
// setup_timer_0(RTCC_INTERNAL | RTCC_DIV_128 );//12.8us long
//
// FUNCTION:
// int32 Infrared_read( void );
// int32 ep_Infrared_read20bit( void );
// int ep_Infrared_measure_pulse( void );
// int ep_Infrared_measure_leader( void );
// int ep_Infrared_measure_hilow( void );
//
////////////////////////////////////////////////////////////////
// Infrared BIT LEVEL
#define Infrared_LOW 0
#define Infrared_HI 1
#define Infrared_LEADER 2
#define Infrared_OUT 3
#define TV_BLUE 0x0f004ba4
#define TV_RED 0x0f004ba5
#define TV_GREEN 0x0f004ba6
#define TV_YELLOW 0x0f004ba7
#define TV_DATA 0x0f004b95
#define TV_TENKEY 0x0f004b8c
#define TV_CH1 0x0c000080
#define TV_CH2 0x0c000081
#define TV_CH3 0x0c000082
#define TV_CH4 0x0c000083
#define TV_CH5 0x0c000084
#define TV_CH6 0x0c000085
#define TV_CH7 0x0c000086
#define TV_CH8 0x0c000087
#define TV_CH9 0x0c000088
#define TV_CH10 0x0c000089
#define TV_CH11 0x0c00008a
#define TV_CH12 0x0c00008b
#define PS2_1 0x14049D00 //Play Station2 リモコン
#define PS2_2 0x14049D01
#define PS2_3 0x14049D02
#define PS2_4 0x14049D03
#define PS2_5 0x14049D04
#define PS2_6 0x14049D05
#define PS2_7 0x14049D06
#define PS2_8 0x14049D07
#define PS2_9 0x14049D08
#define PS2_0 0x14049D09
#define PS2_L1 0x140DAD5A
#define PS2_L2 0x140DAD58
#define PS2_L3 0x140DAD51
#define PS2_R1 0x140DAD5B
#define PS2_R2 0x140DAD59
#define PS2_R3 0x140DAD52
#define PS2_DISPLAY 0x14049D54
#define PS2_MARU 0x140DAD5D //RT
#define PS2_PEKE 0x140DAD5E //RB
#define PS2_SANKAKU 0x140DAD5C //LT
#define PS2_SIKAKU 0x140DAD5F //LB
#define PS2_TIME 0x14049D28
#define PS2_UP 0x14049D79
#define PS2_DOWN 0x14049D7A
#define PS2_LEFT 0x14049D7B
#define PS2_RIGHT 0x14049D7C
#define PS2_START 0x140DAD53
#define PS2_CLEAR 0x14049D0F
#define PS2_ENTER 0x14049D0B
#define PS2_AUDIO 0x14049D64
#define PS2_PROGRAM 0x14049D1F
#define PS2_TITLE 0x14049D1A
/*---------------------------------------------
Infraredを受信して32bitデーターを返す
リピート時間制御:繰り返し時間よりも早くこの関数を
実行すれば即returnする
---------------------------------------------*/
int32 Infrared_read( void )
{
int32 code;
if( ep_Infrared_measure_leader() != Infrared_LEADER ){ // リーダー検出
return(0);
}
code = ep_Infrared_read20bit(); // 12-20ビット入力
return( code );
}
/*---------------------------------------------
Infraredを受信して32bitデーターを返す
必ず、ヘッダー受信の後に実行するようにプログラムしなければならない
global変数 g_Infrared_bit_lengを使う
RETURN:
32bit data
0C 00 00 95 <- POWER ON(12bit)
0F 00 4B CA <- 決定(15bit)
14 04 9D 32 <- DVD再生(20bit)
32ビットの上位8ビットはInfraredのビット長を示し、下位24ビットはコード
---------------------------------------------*/
int32 ep_Infrared_read20bit( void )
{
int a_cnt, a_data;
int32 a_Infrared_bit_leng = 0;
int32 a_code;
int32 a_shift;
a_code = 0;
a_shift = 1;
a_Infrared_bit_leng = 0;
for( a_cnt=0 ; a_cnt<20 ; ++a_cnt ){
a_data = ep_Infrared_measure_hilow();
if( a_data == Infrared_OUT ){
break; // END
}
if( a_data == Infrared_HI ){
a_code |= a_shift;
}
a_shift <<= 1;
++a_Infrared_bit_leng;
}
// BIT長が12,15,20のいずれかであれば有効とする
if( a_Infrared_bit_leng == 12 ||
a_Infrared_bit_leng == 15 ||
a_Infrared_bit_leng == 20 ){
a_code |= (a_Infrared_bit_leng << 24);
}else{
a_code = 0;
}
return( a_code );
}
/*---------------------------------------------
InfraredのLEADERパルスを検出する
LEADERパルスは負の2400us幅である。立下りエッジから300us後
がLOW,600us後がLOW,600us後がLOW,600us後がLOW,600us後がHI
であればLEADERと判断する。
"0" ~~~~|_|~~~~~~~~~~~~~~~~~~~~~~~ width=600us
"1" ~~~~|____|~~~~~~~~~~~~~~~~~~~~ width=1200us
LEADER ~~~~|__________|~~~~~~~~~~~~~~ width=2400us
---------------------------------------------*/
int ep_Infrared_measure_leader( void )
{
int16 uscnt=0;
if( !PORTA.Infrared_rx ){
return( Infrared_OUT );
}
do{
if( uscnt > 400 ){ // uscnt400は約800us
return( Infrared_OUT ); // 800us経過してもLOWにならない
}
delay_us(1);
++uscnt;
}while( PORTA.Infrared_rx ); // LOWになったか
delay_us(300);
if( PORTA.Infrared_rx == 0 ){
delay_us(600);
if( PORTA.Infrared_rx == 0 ){
delay_us(600);
if( PORTA.Infrared_rx == 0 ){
delay_us(600);
if( PORTA.Infrared_rx == 0 ){
delay_us(600);
if( PORTA.Infrared_rx == 1 ){
// ここまでくると本当のLEADERパルス
return( Infrared_LEADER );
}
}
}
}
}
return( Infrared_OUT );
}
/*---------------------------------------------
InfraredのHI又はLOWパルスを検出する
必ずLEADERパルスの直後から呼び出して検出すること
立下りエッジから900us後にHIであれば"0"パルス、LOWであれば
"1"パルスと判断する。
"0" ~~~~|_|~~~~~~~~~~~~~~~~~~~~~~~ width=600us
"1" ~~~~|____|~~~~~~~~~~~~~~~~~~~~ width=1200us
LEADER ~~~~|__________|~~~~~~~~~~~~~~ width=2400us
---------------------------------------------*/
int ep_Infrared_measure_hilow( void )
{
int level;
int16 uscnt=0;
do{
if( uscnt > 400 ){ // uscnt400は約800us
return( Infrared_OUT ); // 800us経過してもLOWにならない
}
delay_us(1);
++uscnt;
}while( PORTA.Infrared_rx ); // LOWになったか
delay_us(900); // LOWのなった瞬間から900us後に
level = PORTA.Infrared_rx; // HI/LOWを読み込む
while( !PORTA.Infrared_rx ); // HIになるまでここで待つ
if( level ){
return( Infrared_LOW );
}else{
return( Infrared_HI );
}
}
//EOF
ありがとうございますm(__)m For アランドロン
ゆういちの上司はアランドロンの髪型と似てるだけじゃなかったけ?
おわり
過去の記事:
猫用自動給餌器をリモコン制御に改造
猫用自動給餌器をリモコン制御に改造、その2
猫用自動給餌器をリモコン制御に改造、その3
人気ブログランキング
2023-12-12 21:12
nice!(10)
コメント(0)
コメント 0