nobcha23の日記

PICマイコンやArduinoを使う電子回路遊びを紹介します

そういうことでコードです

PIC16F1827を使用したキャパシティブセンシングのi2cスレーブのコードです。キーパッド使用したi2cスレーブのプログラムに、簡易式カウンタのプログラムをマージした感じで作りました。

i2cスレーブを作ったとき心を入れ替え、MCCPの割り込みハンドラーを作り直したのが、良かったのかも。

ただし、LCD表示が遅いためLCD表示とi2c制御とキャパシティブセンシングが両立しないのが残念なところです。

デバッグ用のダンプ表示機能などがコンパイルオプションとかコメントアウトで残っておりますが、デバッグ時苦労の後のご愛嬌と思ってください。

/*  //////////////////////////////////////////////////////// /////
 *  PIC16F1827にSC1602互換LCD表示パネルとm-touch仕様のcsmを備え
 *   i2cのスレーブとして表示を行う実験用ルーチンです。  
 *   sc1602とは4ビットモード接続されます。  
 *	
 *	i2cの状態管理はマイクロチップ社AN-734を参考にしました。
 *  状態1-5でmsspからの割り込みをハンドリングします。
 *
 *  	PIC16F88   V0.1 2011.09.30  V1.0 11.10.03 をベースに改造
 *	PIC16F1823 V2.1 2011.11.10  2 NiH operation(CLK OUT)
 *	PIC16F1827 V3.0 2012.8.30
 *	PIC16F1827 V4.0 2012.9.25  LCD+KeyPad Status 1-5
 *	    	   timing tunig for LCD & Key pad connection
 *	PIC16F1827 V5.0 2012.10.5  LCD+4Key csm
 *
 *		MPLAB v8.85 & HI-TECH C V9.83 
 *		by nobcha (c)2012
 *
 *	i2c経由LCDデータは必ず2バイト単位でくるとする。MAX6(2,4,6)
 *	1バイト目はRSビット(コマンド0x0、データ0x40)、2バイト目が
 *	LCDに書き込むデータです
 *	ストロベリーリナックスの拡張コマンドはサポートしてません
 *
 *	コマンドで0x11がキースキャンスタート、0x10がキースキャン
 *	ストップ,キースキャン中はLCDデータを渡してはいけない
 *	i2cのREADコマンドに応答して現在管理しているキーパッドの
 *	情報を返事する。下位の7ビットはキーパッドのasciiコードを示す。
 *	数字は0x30から0x34、
 *	キー入力は20mS間隔のスキャン2回同じコードが続けば有効とする
 *
 *	RA0-3:SC1602は4bit
 *	RA4:CSM1
 *	RA5:MCLR
 *	RA7:動作確認用LED
 * 	RB1:SDA1 MSSP
 *	RB2:CSM2
 *	RB3:CSM3
 *	RB4:SCL1 MSSP
 *	RB5:CSM4
 *	RB6:LCD EN bit (enable)はRB6につなぎます。
 *	RB7:LCD RS bit(RS)はRB7につなぎます。
 *
 *	SC1602 pin connection via 4bit mode
 *		#1	Vdd=5V
 *		#2	Vss=GND
 *		#3	LCD contrust center of 5k VOL
 *		#4	RS	RB7
 *		#5	R/W	GND
 *		#6	EN	RB6
 *		
 *		#11-14	DATA	RA0-3
 *
 * 	TMR1 is gate time controller as set (65536-20000) for 5ms 
 * 
 * 	CLK INT 16MHz	
 *
 * /////////////////////////////////////////////////////////// */
#include	<htc.h>
#include	"lcd.h"

#define _XTAL_FREQ  16000000
#define PIC_CLCK 1600000
#define I2C_ADR 0x7C
#define DEBUG 0					// 1:debug
#define MON_LED LATA7
#define DEF 30

__CONFIG(	
    FOSC_INTOSC & WDTE_OFF & PWRTE_ON & MCLRE_ON & CP_OFF 
    & CPD_OFF & BOREN_OFF & CLKOUTEN_ON & IESO_OFF & FCMEN_OFF
	);

__CONFIG(
	WRT_OFF & PLLEN_OFF & STVREN_ON &  LVP_OFF
	);
/* /////////////////////////////////////////////////  */

// Function prototyping
void	lcd_dispch( unsigned char);		/* 1バイトを2ヘキサでLCDへ */
unsigned char hextoascii(unsigned char);	/* ニブルをヘキサ変換 */
void	init(void);					/* PORT OSC */
void	mssp_init(void);			/* MSSP SSP1CON1 SSP1ADD */
void	csm_init(void);				/* CSM */
int		csm_scan(unsigned char);
void 	cnt_setup(void);
void 	cnt_reset(void);
void 	ssp_handler(void);
void	disp_freq(int);
interrupt i2c_slave(void);

// variable 
unsigned char 	buffer[20];			/* 受信バッファ	*/
unsigned char	stat[20];			/* デバッグ用ステイタスバッファ*/
char	rcv_count=0 ,cnt=0, kint_on, ssp_int_on, lcd_rs_rcv;
unsigned char lcdrs, key_in_p, key_in=0, csm_ch, scan_comp;
char key_in_cnt=0, cnt_start=0, cnt_of;
int nom_freq[4];
int cur_freq[4];
unsigned char	MSG1[] = "i2c csm_key v4";
unsigned char	MSG2[] = "K=";


void main(void){
char	i, j=0;
	init();						/* ポート初期化			*/
	__delay_ms(200);				// Slow LCD start up
	lcd_init();					/* LCDの初期化			*/

	MON_LED=0;	
	ssp_int_on=0;
	__delay_ms(200);
	MON_LED=1;

	if (DEBUG==1){					/*	debug mode		    */
		lcd_goto(0x0);				/* 1行目にLCD動作表示		*/
		lcd_puts(MSG1);				/* LCDに初期表示	 	    */
		for(i=0;i<8;i++){
			MON_LED != RA7;			/* 動作モニター用LED点灯 	*/
			__delay_ms(200);
		}
	}	

	mssp_init();					/* SSPの初期化			*/
	SSP1IF = 0;					/* SSPIF clear			*/
	key_in=0;
	rcv_count=0;					// i2c receive counting

// For debugging set 1
//	cnt_start=1;	
//	csm_ch=1;
	for(i=0;i<4;i++)	cur_freq[i]=0;


	while(1){					/* 繰り返し			*/
		SSP1IE=1;				// MSSP INT ENABLE
		PEIE=1;
		GIE=1;

		if(scan_comp==1){			// all channels scanned end
			cnt_reset();			// counter stop
			GIE=0;				// INT disable
			switch(cnt_start){		// cnt_start=status ;2-5:nominal cal, 6:key check
				case 2: 
					for(i=0;i<4;i++)	nom_freq[i]=cur_freq[i];
					cnt_start=3;
				break;
				case 3:
				case 4:
				case 5:	
					for(i=0;i<4;i++)
						nom_freq[i]=(nom_freq[i]*3)/4+cur_freq[i]/4;
								// calculate nominal freq
// for csm debug
					if (DEBUG==1){		/*	debug mode	*/
						__delay_ms(20);
						lcd_goto(0x4);	// go to top
						__delay_ms(20);
						for(i=0;i<4;i++){
							disp_freq(nom_freq[i]);
							__delay_ms(2);
							lcd_putch(0x2F); 	// delimitter
							__delay_ms(2);
						}
					}		
// debug
					cnt_start++;
				break;
				case 6: 
					for(i=0;i<4;i++){
						if(DEF<(nom_freq[i]-cur_freq[i])) key_in=(i+1)|0x30;
										// defference is more than DEF 
// for csm debug
						if (DEBUG==1){			/*	debug mode		*/
							__delay_ms(20);
							lcd_goto(0x44+(i)*5);	// go to every position of 2nd line
							__delay_ms(20);
							disp_freq(cur_freq[i]);
							lcd_putch(0x2F);
					


							lcd_goto(0x40);			// go to 2nd pos of 2nd line
							lcd_putch(0x30|(j++&0xF0)>>4); 	// op countig
							lcd_putch(key_in);
							lcd_putch(key_in_p);

						}
// debug
					}			
				break;
			}
		scan_comp=0;
		csm_ch=1;
		cnt_setup();

		}
		if(key_in!=00){
			key_in_p=key_in;
			kint_on=1;
		}

		if(cnt_start==1){			// csm activated
			csm_ch=1;			// csm ch initilized
			cnt_setup();			// Start TMR1 gating and TMR0 counting
			cnt_start=2;			// getting csm nominal freq	
			CPSCON1bits.CPSCH=4;		// CPS 4 selected
			CPSCON0bits.CPSRNG=0b11;	// high current


		}
		if(kint_on&DEBUG){			// Key in occuered?
//			GIE=0;
//			lcd_goto(0x51);			// KEY IN MSG 
//			lcd_puts(MSG2);			// K=

//			lcd_dispch(key_in_p);	// 2 HEXA for key code
//			lcd_dispch(key_in_cnt);	// Long keying counter
//			kint_on=0;			// Key in int flag reset
//			GIE=1;
		}

		if(SSP1STATbits.P & rcv_count){	/* 受信のストップ状態チェック	*/
			GIE=0;

/* for i2c debug

		if(DEBUG){				//	debug mode & received
			for(i=0;i<(rcv_count+1);i++){	
				lcd_goto(i*3 );
				lcd_putch(0x23);	// #
				lcd_dispch(buffer[i]);	
//				lcd_dispch(stat[i]);
			}	
			lcd_goto(0x53);			// 2行目にLCD動作表示	
			lcd_putch(cnt++|0x30); 		// display count
			if(cnt>0x05){cnt=0;}
	
			lcd_putch(0x3C);
			lcd_dispch(rcv_count);		// 下位4ビット取り出しASCII 
			lcd_putch(0x3E);
			lcd_putch(0x20);

			for(j=0;j<2;j++){		// 表示時間を待たせる 		
				__delay_ms(100);
			}
		}
   i2c debug end   */
  
			rcv_count=0;
			MON_LED ^= 1; 

		} 
  
	}
}


// Displaying 1 byte via 2 Asciis 
void	lcd_dispch( unsigned char ch_code){
		lcd_putch(hextoascii((ch_code>>4)&0xF));	
		lcd_putch(hextoascii(ch_code&0xF));	
	}

// Converting 4 bits to hexadicimal asciis 
unsigned char hextoascii(unsigned char hex_data){
		hex_data = hex_data | '0';		// ASCII code
		if(hex_data>0x39)hex_data=hex_data+7;	// alphabet
	return hex_data;
	}

// Displaying INT freq data as 4 hex
void disp_freq(int freq){
		unsigned char freq1,freq2;
		freq1=(unsigned char)((freq>>8)&0xFF);
		freq2=(unsigned char)(freq&0xFF);
		lcd_putch(hextoascii((freq1>>4)&0xF));	
		lcd_putch(hextoascii((freq1)&0xF));	
		lcd_putch(hextoascii((freq2>>4)&0xF));	
		lcd_putch(hextoascii(freq2&0xF));		
	}	

// Initializing ports and osc
void	init(void){		
	ANSELA=0;				// all degital
	ANSELB=0;

	PORTA = 0;				// 0 level
	PORTB = 0b11000000;			//	
	TRISA = 0;				// all output
	TRISB = 0;				//	
 	__delay_us(10);				// 10us discharge

	PORTA = 0b00000000;			//
	PORTB = 0b11000000;			//	
 						// port direction: 1:input
	TRISA = 0b01110000;			/* RA0-3は出力、RA4-6入力、RA7出力 */
	TRISB = 0b00111111;	 		/* RB0−5は、sw、i2c用で入力、RB6-7出力設定 */
	OSCCON = 0b01111000;			/* 内部クロック16MHz 		*/

	ANSELA = 0x10;				/* CPS4:RA4 ANALOGUE	*/

	ANSELB = 0x2C;				/* CPS10,9,7:RB2,3,5 ANALOGUE	*/

	OPTION_REG = 0x80;			/* オプション設定なし ~WPUA=1		    */  

	} 
	
// Initializing MSSP function
void	mssp_init(void){
	SSP1CON1 = 0x36;			// b5:SSP1EN,b4:CKP,b3-0:SSP_slave_7bit 
	SSP1ADD = I2C_ADR;			// SSP ADDRESS SET
	PEIE = 1;				// PEIE enable
	SSP1IE = 1;
	}

// Setting TMR1 up
void 	cnt_setup(void){
	TMR1ON=0;				// TMR1 OFF
	CPSON=0;

	TMR0 = 0 ;				// TMR0 clear
	TMR1L = 0;				// Clear Low Byte of TMR1
						// 16MHz 62.5nS*4 250nS 5ms 20000 count 
	TMR1H = 177;				// Set 177*256 + 224
	TMR1L = 224;				// Set (177*256+224) =45536=65536-20000		
	TMR1IF=0;				// TMR1 flag off
	TMR1IE=1;				// TMR1 INT ENABLE
	TMR0IF=0;				// TMR0 flag off
	TMR0IE=1;				// TMR0 INT ENABLE
	PEIE=1;					// PEIE 1
	TMR1ON=1;				// TMR1ON 1
	cnt_of=0;				// TMR0 over flow count
	T0XCS=1;				// TMR0 = CPSCLK
	TMR0CS=1;				// select CPS for TMR0 input
	CPSCON0bits.CPSRNG=0b11;		// OSC is mid
 	CPSON=1;				// CPS enable

	}

// Stop TMR1,0
void cnt_reset(void){
	TMR1IE=0;				// TMR1 INT DISABLE
						// TMR0 INT DISABLE
	PEIE=0;					// PEIE 0
	TMR1ON=0;				// TMR1ON 0
	TMR0=0;
	TMR1L=0;
	TMR1H=0;
	cnt=0;
	cnt_of=0;				// TMR0 over flow count
}

// ssp int handling function
void ssp_handler(void) {
	unsigned char s_state,dummy;
	s_state=SSP1STAT&0b00101101;			// SMP,CKE,-D/A,P,-S,-R/W,UA,-BF

	switch(s_state){		
		case 0b00001001:			// State1 address,S,write,buffer full
			dummy=SSP1BUF;			// if address, read SSPBUF as dummy
			rcv_count=0;
			break;
		case 0b00101001:			// State2 data,write,buffer full
			buffer[rcv_count]=SSP1BUF ;	// 
			if((rcv_count&0x1)==0){		// receiving RS byte and DATA byte as pair
				lcd_rs_rcv=((buffer[rcv_count]>>6)&0x1);	// Set lcd_rs bit
			}
			else if((buffer[rcv_count]==0x10)&(lcd_rs_rcv==0)) {
				cnt_start=0;
			}
			else if((buffer[rcv_count]==0x11)&(lcd_rs_rcv==0)) {
				cnt_start=1;
				key_in=0;
			}
			else if(DEBUG==0) lcd_write_rs(buffer[rcv_count],lcd_rs_rcv);	// Data byte
			stat[rcv_count]=SSP1STAT;	// status buffering
			rcv_count++;			// next buffer
			break;
		case 0b00001100:			// State3 address,S, read,buffer empty
		case 0b00001101:			// State3 address,S, read,buffer full
			SSP1CON1bits.CKP=0;		// CKP clear
			dummy=SSP1BUF;			// dummy address read
			if(key_in_p==key_in){
				SSP1BUF=key_in_p;	// key in data transmitting
			}
			else SSP1BUF=0;			// No key in
			SSP1CON1bits.CKP=1;		// CKP releas
			break;
		case 0b00101100:			// State4 data,S, read,buffer empty
			SSP1CON1bits.CKP=0;		// CKP clear

			SSP1CON1bits.CKP=1;		// CKP releas
			break;
		case 0b00101000:			// State5 data,S, read,buffer empty &full
		case 0b00101101:
			SSP1CON1bits.CKP=1;		// CKP release

			break;
		default:
			lcd_puts("ERR");			
			break;
	}
}

// Interrupu service
interrupt i2c_slave(void){				// i2c slave receive int func
	unsigned char pos=0;
	GIE=0;						// R/W detect? needed  if read send KB_status
	if(TMR1IF==1){					// Gate timeout
		cur_freq[(csm_ch++)-1]=(int)(TMR0)+(int)(cnt_of)*256;	// Count freq next ch++
		cnt_reset();
		if(csm_ch>5){				// Next ch > 5, scan end (csn_ch:0-3)
			csm_ch=1;				// csm_ch 1-4 going around
			scan_comp=1;			// scan end
		}
		switch(csm_ch){				// select cpsch
			case 1: CPSCON1bits.CPSCH=4;
			break;
			case 2: CPSCON1bits.CPSCH=10;
			break;
			case 3: CPSCON1bits.CPSCH=9;
			break;
			case 4: CPSCON1bits.CPSCH=7;
			break;
		}
		cnt_setup();				// next counting
	}
	if(TMR0IF==1){
		cnt_of++;				// TMR0 over flow
		TMR0IF=0;
	}

	if(SSP1IF==1){
		SSP1IE=0;
		SSP1IF=0;
		ssp_handler();

		SSP1IE=1; 
	}

	GIE=1;
}

はてなダイヤリーでのソースコードの貼り付け方という掟があって、今回対応いたしました。ばんとさんご指導ありがとうございます。調べていただき感謝します。