FC2カウンター FPGAの部屋 PIC16F1825 の使い方3(I2C 機能、割り込み編)
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

PIC16F1825 の使い方3(I2C 機能、割り込み編)

PIC16F1825 の使い方2(USART の設定)”の続き。

PIC マイコンの PIC16F1825 を使うことになったので、使い方の覚書を書いておく。
前回は、USART を使ったが、今回は、I2C 機能を割り込みで使用したが、I2C Read を連続して行うと 1 分くらいで止まってしまう。

I2C は PIC16F1825 マスタで 3 軸加速度センサーの ADXL355 をスレーブとして接続している。
現在はブレッドボード上でジャンパー・ワイヤで接続している。
PIC_7_210317.jpg

割り込みを使用した acc3_uart.c を示す。
なおクロック周波数は最高速の 32 MHz で I2C の動作周波数は 400 kHz に設定されている。

/*
 * File:   acc3_uart.c
 * Author: ono
 *
 * Created on 2021/02/03, 11:09
 */

#include <xc.h>
#include <stdint.h>

#pragma config FOSC = INTOSC
//#pragma config WDTEN = OFF
#pragma config LVP = OFF
#pragma config PWRTE = ON

unsigned char chr;
unsigned int n = 0;
uint8_t I2C_rw = 1; // 0 - Read, 1 - Write
#define I2C_READ    0
#define I2C_WRITE   1

uint8_t I2C_Read_bytes = 3; // 3 bytes or 1 byte

uint8_t I2C_WState = 0;
uint8_t I2C_RState = 0;
#define I2CW_IDLE               0
#define I2CW_SENT_START         1
#define I2CW_SENT_DEVICE_ADDR   2
#define I2CW_SENT_REG_ADDR      3
#define I2CW_SENT_DATA          4
#define I2CW_SENT_STOP          5

#define I2CR_IDLE                   0
#define I2CR_SENT_START             1
#define I2CR_SENT_DEVICE_ADDR       2
#define I2CR_SENT_REG_ADDR          3
#define I2CR_SENT_REPEAT_START      4
#define I2CR_SENT_DEV_RADDR         5
#define I2CR_RECV_DATA0_WACK        6
#define I2CR_RECV_DATA0_WACK_DET    7
#define I2CR_RECV_DATA0_WNAK        8
#define I2CR_RECV_DATA1             9
#define I2CR_RECV_DATA1_WACK_DET    10
#define I2CR_RECV_DATA2_WNAK        11
#define I2CR_SEND_STOP              12
#define I2CR_SENT_STOP              13

uint8_t accs_dev_addr, accs_reg_addr, accs_write_data;
uint8_t accs_recv_data0, accs_recv_data1, accs_recv_data2;

void Delay(unsigned int m)
{
    for(n=0;n<m;n++);       //Delay
}

void acc_sensor_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t write_data);
void acc_sensor_recv(uint8_t dataX[3], uint8_t dataY[3], uint8_t dataZ[3]);
void acc_sensor_recv_xyz(uint8_t dev_addr, uint8_t reg_addr, uint8_t data[3]);

void main( void )
{
    OSCCON = 0b11110000; // 4x PLL enable, 8MHz or 32MHz, FOSC
    OSCTUNE = 0x00;
    
    TRISA = 0;
    TRISC = 0x23; // SCL, SDA, UART RX is input setting.
    ANSELA = 0;
    ANSELC = 0;

    //I2C
    SSP1ADD   = 19;     //speed 400KHz
    //SSP1ADD   = 0x4F; //speed 100KHz
    SSP1STAT = 0b10000000; // 400kHz
    SSP1CON1 = 0b00101000;  //SSP1EN=1, Master mode
    SSP1CON2 = 0b00000000;
    SSP1CON3 = 0b00000000;
    SSP1IE = 1;
    SSP1IF = 0;
    
    // UART
    BAUDCON = 0; // BRG16=0: 8bit counter mode
    SPBRGL = 3; // SYNC=0, BRGH=1, BRG16=0, 500000bps
    TXSTA = 0b00100100; // TXEN=1, SYNC=0, BRGH=1
    RCSTA = 0b10010000; // SPEN=1, CREN=1
    //INTCON = 0b11000000; // GIE=1, PEIE=1
    RCIE = 1;
    RCIF = 0;
    PEIE = 1;
    GIE = 1;

     /*while(1){
        while(TRMT == 0);
        TXREG = 0x55;
              
        Delay(50);
    } */
    
    uint8_t dataX[3], dataY[3], dataZ[3];
    uint8_t count=0, count_b;
    
    /*while(1){
        acc_sensor_write(0x3a, 0x2c, 0x83); // I2C speed is Hi speed, +-8g
        Delay(500);
    }*/
    
    acc_sensor_write(0x3a, 0x2c, 0x83); // I2C speed is Hi speed, +-8g
    
    acc_sensor_write(0x3a, 0x1e, 0x00); // OFFSET_X_H
    acc_sensor_write(0x3a, 0x1f, 0x00); // OFFSET_X_L
    acc_sensor_write(0x3a, 0x20, 0x00); // OFFSET_Y_H
    acc_sensor_write(0x3a, 0x21, 0x00); // OFFSET_Y_L
    acc_sensor_write(0x3a, 0x22, 0x00); // OFFSET_Z_H
    acc_sensor_write(0x3a, 0x23, 0x00); // OFFSET_Z_L
    
    acc_sensor_write(0x3a, 0x2d, 0x00); // stanby clear
    
    while(1){
        acc_sensor_recv_xyz(0x3b, 0x08, dataX);
        //acc_sensor_recv(dataX, dataY, dataZ);
        Delay(100);
    }
    
}

void interrupt RX_int(void){
    if(RCIF == 1){
        RCIF = 0;
        chr = RCREG;
        if(RCREG != 0x55)
            return;
              
        while(TRMT == 0);
        TXREG = chr + 1;
    } else if(SSP1IF == 1){
        if(I2C_rw == I2C_WRITE){
            switch(I2C_WState){
                case I2CW_SENT_START:
                    SSP1IF = 0;
                    SSP1BUF = accs_dev_addr & 0xFE; // dev_addr to SSP1BUF for Write
                    I2C_WState = I2CW_SENT_DEVICE_ADDR;
                    break;
                case I2CW_SENT_DEVICE_ADDR:
                    SSP1IF = 0;
                    SSP1BUF = accs_reg_addr; // reg_addr to SSP1BUF
                    I2C_WState = I2CW_SENT_REG_ADDR;
                    break;
                case I2CW_SENT_REG_ADDR:
                    SSP1IF = 0;
                    SSP1BUF = accs_write_data; // write_data to SSP1BUF
                    I2C_WState = I2CW_SENT_DATA;
                    break;
                case I2CW_SENT_DATA:
                    SSP1IF = 0;
                    SSP1CON2bits.PEN = 1; //Initiate stop condition
                    I2C_WState = I2CW_SENT_STOP;
                    break;
                case I2CW_SENT_STOP:
                    SSP1IF = 0;
                    I2C_WState = I2CW_IDLE;
                    break;
                default:
                    break;
            }
        } else { // I2C_rw == I2C_READ;
            switch(I2C_RState){
                case I2CR_SENT_START:
                    SSP1IF = 0;
                    SSP1BUF = accs_dev_addr & 0xFE; // dev_addr to SSP1BUF for Write
                    I2C_RState = I2CR_SENT_DEVICE_ADDR;
                    break;
                case I2CR_SENT_DEVICE_ADDR:
                    SSP1IF = 0;
                    SSP1BUF = accs_reg_addr; // reg_addr to SSP1BUF
                    I2C_RState = I2CR_SENT_REG_ADDR;
                    break;
                case I2CR_SENT_REG_ADDR:
                    SSP1IF = 0;
                    SSP1CON2bits.RSEN = 1; // Repeat Star
                    I2C_RState = I2CR_SENT_REPEAT_START;
                    break;
                case I2CR_SENT_REPEAT_START:
                    SSP1IF = 0;
                    SSP1BUF = accs_dev_addr | 1; // dev_addr to SSP1BUF for Read
                    I2C_RState = I2CR_SENT_DEV_RADDR;
                    break;
                case I2CR_SENT_DEV_RADDR:
                    SSP1IF = 0;
                    SSP1CON2bits.RCEN = 1; // Recieve Enable
                    if(I2C_Read_bytes == 3)
                        I2C_RState = I2CR_RECV_DATA0_WACK;
                    else
                        I2C_RState = I2CR_RECV_DATA0_WNAK;
                    break;                    
                case I2CR_RECV_DATA0_WNAK:
                    SSP1IF = 0;
                    SSP1CON2bits.ACKDT = 1; // NACK
                    SSP1CON2bits.ACKEN = 1;
                    accs_recv_data0 = SSP1BUF; //Read data from SSP1BUF
                    I2C_RState = I2CR_SEND_STOP;
                    break;
                case I2CR_RECV_DATA0_WACK:
                    SSP1IF = 0;
                    SSP1CON2bits.ACKDT = 0; // ACK
                    SSP1CON2bits.ACKEN = 1;
                    accs_recv_data0 = SSP1BUF; //Read data from SSP1BUF
                    I2C_RState = I2CR_RECV_DATA0_WACK_DET;
                    break;
                case I2CR_RECV_DATA0_WACK_DET:
                    SSP1IF = 0;
                    SSP1CON2bits.RCEN = 1; // Recieve Enable
                    I2C_RState = I2CR_RECV_DATA1;
                    break;
                case I2CR_RECV_DATA1:
                    SSP1IF = 0;
                    SSP1CON2bits.ACKDT = 0; // ACK
                    SSP1CON2bits.ACKEN = 1;
                    accs_recv_data1 = SSP1BUF; //Read data from SSP1BUF
                    I2C_RState = I2CR_RECV_DATA1_WACK_DET;
                    break;
                case I2CR_RECV_DATA1_WACK_DET:
                    SSP1IF = 0;
                    SSP1CON2bits.RCEN = 1; // Recieve Enable
                    I2C_RState = I2CR_RECV_DATA2_WNAK;
                    break;
                case I2CR_RECV_DATA2_WNAK:
                    SSP1IF = 0;
                    SSP1CON2bits.ACKDT = 1; // NACK
                    SSP1CON2bits.ACKEN = 1;
                    accs_recv_data2 = SSP1BUF; //Read data from SSP1BUF
                    I2C_RState = I2CR_SEND_STOP;
                    break;
                case I2CR_SEND_STOP:
                    SSP1IF = 0;
                    SSP1CON2bits.PEN = 1; //Initiate stop condition
                    I2C_RState = I2CR_SENT_STOP;
                    break;
                case I2CR_SENT_STOP:
                    SSP1IF = 0;
                    I2C_RState = I2CR_IDLE;
                    break;
                default:
                    break;
            }
        }
    }
}

void acc_sensor_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t write_data){
    while(I2C_WState != I2CW_IDLE);
    while(I2C_RState != I2CR_IDLE);
    
    accs_dev_addr = dev_addr;
    accs_reg_addr = reg_addr;
    accs_write_data = write_data;
    I2C_rw = I2C_WRITE;
    I2C_WState = I2CW_SENT_START;
    
    SSP1CON2bits.SEN = 1;           //Initiate start condition
    
    while(I2C_WState != I2CW_IDLE); // Wait I2C Write
    //Delay(10);
}

void acc_sensor_recv(uint8_t *dataX, uint8_t *dataY, uint8_t *dataZ){
    acc_sensor_recv_xyz(0x3b, 0x08, dataX);
    acc_sensor_recv_xyz(0x3b, 0x0b, dataY);
    acc_sensor_recv_xyz(0x3b, 0x0e, dataZ);
}

void acc_sensor_recv_xyz(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data){
    while(I2C_WState != I2CW_IDLE);
    while(I2C_RState != I2CR_IDLE);
    
    do{ // Wait data ready
        accs_dev_addr = 0x3b;
        accs_reg_addr = 0x04; // Watch data ready
        I2C_rw = I2C_READ;
        I2C_RState = I2CR_SENT_START;
        I2C_Read_bytes = 1; // 3 bytes or 1 byte
        SSP1CON2bits.SEN = 1;           //Initiate start condition
        while(I2C_RState != I2CR_IDLE); // Wait I2C Read
    }while((accs_recv_data0 & 0x01) != 0x01);

    //Delay(10);
    accs_dev_addr = dev_addr;
    accs_reg_addr = reg_addr;
    I2C_rw = I2C_READ;
    I2C_RState = I2CR_SENT_START;
    I2C_Read_bytes = 3; // 3 bytes or 1 byte
    SSP1CON2bits.SEN = 1;           //Initiate start condition
    while(I2C_RState != I2CR_IDLE); // Wait I2C Read
    
    data[0] = accs_recv_data0;
    data[1] = accs_recv_data1;
    data[2] = accs_recv_data2;
    //Delay(10);
}


この中の

acc_sensor_write(0x3a, 0x2c, 0x83); // I2C speed is Hi speed, +-8g

の波形を示す。
この波形を見る限り良さそうだ。
PIC_1_210317.jpg

I2C Write は大丈夫そうなのだが、 I2C Read を入れるとある程度動作して、止まってしまう。
I2C Read も割り込みハンドラのステート毎にちゃんと動作しているかを MPLAB X で調べてみたが、ちゃんと遷移している。

I2C の処理を割り込みハンドラで実装してダメだったので、次回はポーリングで実装してみよう。
  1. 2021年03月18日 03:38 |
  2. PIC
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/5181-60d60447
この記事にトラックバックする(FC2ブログユーザー)