PIC12F675は、I2Cの信号を発生する機能を持っていません。そこで、ソフト的にI2Cを行うことになります。
あちこちのWebサイトを参考にして、start condition, 1 bye 送信,stop conditionのプログラムを組み、0b10101010を送って、波形を観察してみました。
SCLのパルス幅は10usです。速度は約20kHzです。
ACKは、スレーブから応答があった時は、SDAが0になります。
大丈夫そうなので、このソフトI2Cと以前に試したシリアル通信を組み合わせて、I2C アドレス スキャナを作ってみました。表示の仕方は、Travel and softwareというサイトを参考にさせていただきました。
今回は、秋月電子のI2C LCDモジュールAQM0802Aのアドレスを調べてみます。
回路図です。
I2Cバスのプルアップ抵抗は、AQM0802Aモジュールに内蔵されていますので、つけていません。
AQM0802AのVDDは3.3Vなので、単3電池2本で電源としています。
SWを押すとscanが始まります。
ブレッドボードです。
スキャン結果は、TeraTermに表示されます。Baudrateは1200bpsです。
ACKが0の時、アドレスに[ ]がつきます。なお、アドレス00~07と78~7Fはシステムで予約されたアドレスですので、スキャンしません。
3Eに[ ]がついています。AQM0802Aのスレーブアドレスは、3Eです。
実際にアドレスとして送るときには、3Eを左に1ビットシフトし LSBに0 をセットして、7Cを送ります。
プログラムです。シリアル通信は、送信だけにしています。
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = INTRCIO // 内蔵クロック4MHz
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config FOSC = INTRCIO // 内蔵クロック4MHz
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#define _XTAL_FREQ 4000000
#define TX GPIO0
#define RX GPIO1
#define SW GPIO2
#define SDA GPIO4
#define SCL GPIO5
#define SDA_ON GPIO4 = 1
#define SDA_OFF GPIO4 = 0
#define SCL_ON GPIO5 = 1
#define SCL_OFF GPIO5 = 0
#define SDA_OUT TRISIO4 = 0
#define SDA_IN TRISIO4 = 1
#define SCL_OUT TRISIO5 = 0
#define SCL_IN TRISIO5 = 1
#define wait __delay_us(10)
#define Baudrate 1200
#define OneBitDelay (1000000 / Baudrate)
#define HalfBitDelay (1000000 / Baudrate / 2)
#define start_addr 0b00000111
#define end_addr 0b01111000
void serial_init(){
TRISIO0 = 0; //GP0 output
TRISIO1 = 1; //GP1 input
TX = 1; //アイドル状態でTXpin High
}
TRISIO0 = 0; //GP0 output
TRISIO1 = 1; //GP1 input
TX = 1; //アイドル状態でTXpin High
}
void putch(unsigned char byte){
// Send start bit
TX = 0;
__delay_us(OneBitDelay);
for(unsigned char i=0;i<8;i++){
if(byte & 0x1){
TX = 1;
}else{
TX = 0;
}
__delay_us(OneBitDelay);
byte >>=1;
}
//Send stop bit
TX = 1;
__delay_us(OneBitDelay);
}
// Send start bit
TX = 0;
__delay_us(OneBitDelay);
for(unsigned char i=0;i<8;i++){
if(byte & 0x1){
TX = 1;
}else{
TX = 0;
}
__delay_us(OneBitDelay);
byte >>=1;
}
//Send stop bit
TX = 1;
__delay_us(OneBitDelay);
}
void I2C_init(){
SDA_IN;
SCL_IN;
SDA_ON;
SCL_ON;
}
SDA_IN;
SCL_IN;
SDA_ON;
SCL_ON;
}
void I2C_start(){
SDA_OUT;
SCL_OUT;
SDA_ON;
SCL_ON;
wait;
SDA_OFF;
wait;
SCL_OFF;
}
SDA_OUT;
SCL_OUT;
SDA_ON;
SCL_ON;
wait;
SDA_OFF;
wait;
SCL_OFF;
}
void I2C_stop(){
SDA_OFF;
SCL_OFF;
wait;
SCL_ON;
wait;
SDA_ON;
SDA_IN;
SCL_IN;
}
SDA_OFF;
SCL_OFF;
wait;
SCL_ON;
wait;
SDA_ON;
SDA_IN;
SCL_IN;
}
unsigned char I2C_write(unsigned char byte){
unsigned char ACK = 0;
for(unsigned char i=0;i<8;i++){
SCL_OFF;
wait;
if(byte & 0x80){
SDA_ON;
}else{
SDA_OFF;
}
byte <<=1;
wait;
SCL_ON;
wait;
SCL_OFF;
}
//Get ACK
SDA_IN;
wait;
SCL_ON;
wait;
ACK = SDA;
SCL_OFF;
SDA_OUT;
unsigned char ACK = 0;
for(unsigned char i=0;i<8;i++){
SCL_OFF;
wait;
if(byte & 0x80){
SDA_ON;
}else{
SDA_OFF;
}
byte <<=1;
wait;
SCL_ON;
wait;
SCL_OFF;
}
//Get ACK
SDA_IN;
wait;
SCL_ON;
wait;
ACK = SDA;
SCL_OFF;
SDA_OUT;
return ACK;
}
}
void main() {
CMCON = 0x07; //コンパレータを使わない
ANSEL = 0x00; //アナログポートを使わない
TRISIO = 0b00110110; //GP1,GP2,GP4,GP5 input GP0,GP3 output
I2C_init(); //I2C初期化
serial_init(); //シリアル通信初期化
while(SW); //SWが押されたらscan start
printf("I2C Address Scan\r\n");
for(unsigned char i=0;i<0x7f;i+=0x10){
for(unsigned char j=0;j<=0xf;j++){
unsigned char i2c_adr = i+j;
if(i2c_adr <= start_addr || i2c_adr >= end_addr){
printf(" -- ");
}else{
I2C_start();
unsigned char acknowledge = I2C_write(i2c_adr<<1);
I2C_stop();
if(acknowledge == 0){
printf("[%02X]",i2c_adr);
}else{
printf(" %02X ",i2c_adr);
}
}
}
printf("\r\n");
}
while(1){
}
}