FC2カウンター FPGAの部屋 PIC
fc2ブログ

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

FPGAの部屋

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

PIC16F1825 の使い方4(SPI 機能)

PIC16F1825 の使い方3(I2C 機能、ポーリング編)”の続き。

PIC マイコンの PIC16F1825 を使うことになったので、使い方の覚書を書いておく。
前回は、I2C をポーリングで書き直したが、やはり数分程度で I2C Read が止まってしまった。I2C はあまり良くないかも知れないので、SPI 機能を使うことにした。
3軸加速度センサーの ADXL355 も SPI モードがあるので、 ADXL355 も SPI モードにして使用する。
ADXL355モジュール説明書 によると、 MISO、 ~CS、 SCK、 MOSI の各端子を PIC16F1825 と接続する。
PIC の SPI 接続を示す。

SPI Master : SCLK - RC0(10 pin), SDI - RC1(9 pin), SDO - RC2(8 pin), /CS(GPIO) - RA2(11 pin)

PIC の SDI は ADXL355 の MISO に接続し、 SDO は ADXL355 の MOSI に接続している。後は同じ名前のピンを接続した。なお、PIC の SPI のピン配置はデフォルトの SPI ピンを使用している。

SPI インターフェースについては、”SPIの基本を学ぶ”を参照。
ADXL355日本語説明書の図64 から図67 を引用する。
PIC_15_210324.png

そうそう、PIC の SPI 設定で勘違いしていることがあった。
SPI Read のときに SCK が出ていなかった。てっきり図64 と図66 で R/W を 1 にすれば Read データが出てくるものと思っていたのだが、出てこなかった。なぜ〜? と思ってオシロスコープ画面をよく見たのだが、出ていなかった。もしかして?と思って SSP1BUF にダミーデータとして 0 を書いた(つまり、SDO から 0 を Write した)ところ、SCK が出て SDI に ADXL355 からの Read データが出てきた。つまり、SPI Read する時は、何かダミーデータを SPI Write する必要があるようだ。そうすると SCK が出力されて、SDI に Read データが出てくる。

それを踏まえて、作ったソースコードを貼っておく。

/*
 * File:   acc3_uart.c
 * Author: ono
 *
 * Created on 2021/02/03, 11:09
 */
// USART :  RX - RC5(5 pin), TX - RC4(6 pin)(default)
// SPI Master : SCLK - RC0(10 pin), SDI - RC1(9 pin), SDO - RC2(8 pin), /CS(GPIO) - RA2(11 pin)
// acc_sensor : ADXL355

#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;

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

void acc_sensor_write(uint8_t reg_addr, uint8_t write_data);
void acc_sensor_grecv(uint8_t dataX[3], uint8_t dataY[3], uint8_t dataZ[3]);
uint8_t acc_sensor_data_ready();
uint8_t acc_sensor_read_byte(uint8_t reg_addr);
void acc_sensor_read_3bytes(uint8_t reg_addr, uint8_t *data);

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

    // SPI
    SSP1STAT = 0b11000000; // SMP=1, CKE=1
    SSP1CON1 = 0b00100010;  //SSP1EN=1, CKP=0, SPI Master, Clock=Fosc/64(500kHz)
    SSP1CON3 = 0b00000000;
    //SSP1IE = 1;
    //SSP1IF = 0;
    RA2 = 1; // SPI /CS = 1
    
    // 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(0x2c, 0x83); // I2C speed is Hi speed, +-8g
        Delay(500);
    }*/
    
    acc_sensor_write(0x2c, 0x83); // I2C speed is Hi speed, +-8g
    
    acc_sensor_write(0x1e, 0x00); // OFFSET_X_H
    acc_sensor_write(0x1f, 0x00); // OFFSET_X_L
    acc_sensor_write(0x20, 0x00); // OFFSET_Y_H
    acc_sensor_write(0x21, 0x00); // OFFSET_Y_L
    acc_sensor_write(0x22, 0x00); // OFFSET_Z_H
    acc_sensor_write(0x23, 0x00); // OFFSET_Z_L
    
    acc_sensor_write(0x2d, 0x00); // stanby clear
    
    while(1){
        while((acc_sensor_data_ready() & 0x01) == 0);
        acc_sensor_read_3bytes(0x08, dataX);
        //acc_sensor_grecv(dataX, dataY, dataZ);
        Delay(500);
    }
    
}

void interrupt RX_int(void){
    if(RCIF == 1){
        RCIF = 0;
        chr = RCREG;
        if(RCREG != 0x55)
            return;
              
        while(TRMT == 0);
        TXREG = chr + 1;
    }
}

void acc_sensor_write(uint8_t reg_addr, uint8_t write_data){
    RA2 = 0; // SPI /CS = 0
    SSP1IF = 0;
    SSP1BUF = (reg_addr<<1);
    while(SSP1IF == 0);
    SSP1IF = 0;
    SSP1BUF = write_data;
    while(SSP1IF == 0);
    RA2 = 1; // SPI /CS = 1
}

void acc_sensor_grecv(uint8_t *dataX, uint8_t *dataY, uint8_t *dataZ){
    while((acc_sensor_data_ready() & 0x01) == 0);
    
    acc_sensor_read_3bytes(0x08, dataX);
    acc_sensor_read_3bytes(0x0b, dataY);
    acc_sensor_read_3bytes(0x0e, dataZ);
}

uint8_t acc_sensor_data_ready(){
    return(acc_sensor_read_byte(0x04));
}

uint8_t acc_sensor_read_byte(uint8_t reg_addr){
    uint8_t data;
    
    RA2 = 0; // SPI /CS = 0
    SSP1IF = 0;
    SSP1BUF = (reg_addr<<1) | 1;
    while(SSP1IF == 0);
    
    SSP1IF = 0;
    SSP1BUF = 0;
    while(SSP1IF == 0);
    data = SSP1BUF;
    RA2 = 1; // SPI /CS = 1
    return(data);
}
void acc_sensor_read_3bytes(uint8_t reg_addr, uint8_t *data){
    RA2 = 0; // SPI /CS = 0
    SSP1IF = 0;
    SSP1BUF = (reg_addr<<1) | 1;
    while(SSP1IF == 0);
    
    SSP1IF = 0;
    SSP1BUF = 0;
    while(SSP1IF == 0);
    data[0] = SSP1BUF;
    
    SSP1IF = 0;
    SSP1BUF = 0;
    while(SSP1IF == 0);
    data[1] = SSP1BUF;
    
    SSP1IF = 0;
    SSP1BUF = 0;
    while(SSP1IF == 0);
    data[2] = SSP1BUF;
    RA2 = 1; // SPI /CS = 1
}


SPI クロックは 32 MHz クロックの 1/64 倍の 500 kHz に設定してある。

これで data_ready の値を取得するために、1バイト Read してから、3 バイト Read している波形を示す。
最初に SCK と SDO の波形を示す。
PIC_9_210323.jpg
PIC_10_210323.jpg
PIC_11_210323.jpg
7 ビットの 0x04アドレスと R/W ビットを連結して ADXL355 に Write して、次に Read データを受け取っている。次に 0x08 アドレスの Read を行って、3 バイト X 軸加速度値を Read している。

次に同じアクセスの SCK と SDI の波形を示す。こちらが ADXL355 からの X 軸加速度値だ。
PIC_12_210323.jpg
PIC_13_210323.jpg
PIC_14_210323.jpg

0x04アドレスの値は 0x07 で data ready ビットは立っていたので、3 バイト X 軸加速度値を Read して、その値は、0xFF、 0xF6、 0xA0 だった。つまり、0xFFF6A が X 軸加速度値となる。
  1. 2021年03月24日 04:31 |
  2. PIC
  3. | トラックバック:0
  4. | コメント:0

PIC16F1825 の使い方3(I2C 機能、ポーリング編)

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

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

ポーリングを使用した acc3_uart.c を示す。
このポーリング編のライブラリ・コードは”【PIC】I2C通信のやり方”のライブラリのレジスタを変更して引用している。

/*
 * File:   acc3_uart.c
 * Author: ono
 *
 * Created on 2021/02/03, 11:09
 */
 // "【PIC】I2C通信のやり方”のライブラリを引用している
 //  https://rikeden.net/?p=36

#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;

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

void I2C_Master_Wait();
void I2C_Master_Start();
void I2C_Master_RepeatedStart();
void I2C_Master_Stop();
void I2C_Master_Write(uint8_t d);
uint8_t I2C_Master_Read(uint8_t a);
void acc_sensor_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t write_data);
void acc_sensor_grecv(uint8_t dataX[3], uint8_t dataY[3], uint8_t dataZ[3]);
uint8_t acc_sensor_data_ready(uint8_t dev_addr);
uint8_t acc_sensor_read_byte(uint8_t dev_addr, uint8_t reg_addr);
void acc_sensor_read_3bytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data);

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){
        while((acc_sensor_data_ready(0x3b) & 0x01) == 0);
        //acc_sensor_read_3bytes(0x3b, 0x08, dataX);
        acc_sensor_grecv(dataX, dataY, dataZ);
        Delay(1000);
    }
    
}

void interrupt RX_int(void){
    if(RCIF == 1){
        RCIF = 0;
        chr = RCREG;
        if(RCREG != 0x55)
            return;
              
        while(TRMT == 0);
        TXREG = chr + 1;
    }
}

void acc_sensor_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t write_data){
    I2C_Master_Start();
    I2C_Master_Write(dev_addr);
    I2C_Master_Write(reg_addr);
    I2C_Master_Write(write_data);
    I2C_Master_Stop();
}

void acc_sensor_grecv(uint8_t *dataX, uint8_t *dataY, uint8_t *dataZ){
    while((acc_sensor_data_ready(0x3b) & 0x01) == 0);
    
    acc_sensor_read_3bytes(0x3b, 0x08, dataX);
    acc_sensor_read_3bytes(0x3b, 0x0b, dataY);
    acc_sensor_read_3bytes(0x3b, 0x0e, dataZ);
}

uint8_t acc_sensor_data_ready(uint8_t dev_addr){
    return(acc_sensor_read_byte(dev_addr, 0x04));
}

uint8_t acc_sensor_read_byte(uint8_t dev_addr, uint8_t reg_addr){
    uint8_t data;
    
    I2C_Master_Start();
    I2C_Master_Write(dev_addr & 0xFE);
    I2C_Master_Write(reg_addr);
    I2C_Master_RepeatedStart();
    I2C_Master_Write(dev_addr | 0x01);
    data = I2C_Master_Read(1);
    I2C_Master_Stop();
    return(data);
}
void acc_sensor_read_3bytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data){
    I2C_Master_Start();
    I2C_Master_Write(dev_addr & 0xFE);
    I2C_Master_Write(reg_addr);
    I2C_Master_RepeatedStart();
    I2C_Master_Write(dev_addr | 0x01);
    data[0] = I2C_Master_Read(0);
    data[1] = I2C_Master_Read(0);
    data[2] = I2C_Master_Read(1);
    I2C_Master_Stop();
}
void I2C_Master_Wait(){
  while ((SSP1STAT & 0x04) || (SSP1CON2 & 0x1F)); //Transmit is in progress
}
void I2C_Master_Start(){
  I2C_Master_Wait();    
  SSP1CON2bits.SEN = 1;           //Initiate start condition
}
void I2C_Master_RepeatedStart(){
  I2C_Master_Wait();
  SSP1CON2bits.RSEN = 1;          //Initiate repeated start condition
}
void I2C_Master_Stop(){
  I2C_Master_Wait();
  SSP1CON2bits.PEN = 1;           //Initiate stop condition
}
void I2C_Master_Write(uint8_t d){
  I2C_Master_Wait();
  SSP1BUF = d;                    //Write data to SSP1BUF
}
uint8_t I2C_Master_Read(uint8_t a){
  uint8_t temp;
  I2C_Master_Wait();
  SSP1CON2bits.RCEN = 1;
  I2C_Master_Wait();
  temp = SSP1BUF;                 //Read data from SSP1BUF
  I2C_Master_Wait();
  SSP1CON2bits.ACKDT = a;         //Acknowledge bit    1:NACK  0:ACK
  SSP1CON2bits.ACKEN = 1;         //Acknowledge sequence
  return temp;
}


acc_sensor_read_byte(dev_addr, 0x04)

の 1 バイトの Read のトランザクションを示す。
PIC_5_210317.jpg
PIC_6_210317.jpg

0x07 が Read できた。

次に、

acc_sensor_read_3bytes(0x3b, 0x08, dataX);

の 3 バイト Read トランザクションを示す。
PIC_2_210317.jpg
PIC_3_210317.jpg
PIC_4_210317.jpg

0x00, 0x1F, 0x70 が Read できた。
しかしやはり、連続 Read していると 1 分くらいで止まってしまう。。。
  1. 2021年03月19日 03:36 |
  2. PIC
  3. | トラックバック:0
  4. | コメント:0

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

PIC16F1825 の使い方2(USART の設定)

PIC16F1825 の使い方1(32MHz クロックとポートの設定)”の続き。

PIC マイコンの PIC16F1825 を使うことになったので、使い方の覚書を書いておく。
今回は、USART つまり UART の設定について書いておく。

PIC16F1825 は秋月電子で販売しているそのマニュアルがこちら。

また、ほぼ互換品の”PIC12(L)F1822/PIC16(L)F1823 データ シート”日本語資料が Microchip にある。

SPBRGL レジスタの SYNC=0, BRGH=1、BAUDCON レジスタの BRG16=0: 8bit counter の時のボーレートの計算式は、

Fosc/(16 * (n + 1)) = 32000000 / (16 * (3 + 1)) = 500000 bps = 500 kbps


USART の設定

// 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
// USART 割り込みありの場合
RCIE = 1;
RCIF = 0;
PEIE = 1;
GIE = 1;


SPEN:シリアルポート・イネーブル 1 - シリアルポートを有効にする
CREN:連続受信イネーブルビット 1 - レシーバーを有効にする
PIE1: 周辺機能割り込みイネーブル レジスタ 1  bit 5  RCIE: USART 受信割り込みイネーブルビット
PIR1: 周辺機能割り込み要求レジスタ 1    bit 5  RCIF: USART 受信割り込みフラグビット 1 - 受信割り込みを保留中
INTCON: 割り込み制御レジスタ        bit 6  PEIE: 周辺機能割り込みイネーブルビット 1 - 全てのアクティブな周辺機能割り込みを有効にする
                     bit 7  GIE: グローバル割り込みイネーブルビット

USART の TX と RX は 2 種類のピン・アサインをすることができる。
TX ー RC4、 RA0
RX ー RC5、 RA1

RC4, RC5 の組になるか、RA0, RA1 の組になるかは APFCON レジスタの値できまる。リセット後のピン・アサインは RC4, RC5 になっているので、それを使用する。
RC5 が USART の RX に割り当たるため TRISC の bit 5 を 1 (入力)にしている。

割り込みを使用しない場合のTX - RX ループバック・テスト
0x55 を送信して、受信したら +1 して、0x56 を送信するソフトウェア(割り込み無し)

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

#include <xc.h>

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

unsigned char chr;
unsigned int n = 0;

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

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   = 0x13;        //speed 400KHz
    SSP1STAT = 0b00000000; // 400kHz
    SSP1CON1 = 0b00101000;    //SSP1EN=1, Master mode
    SSP1CON2 = 0b00000000;

        // 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

     while(1){
        while(TRMT == 0);
        TXREG = 0x55;
        
        while(RCIF == 0);
        RCIF = 0;
        chr = RCREG;
              
        //Delay(10);
        
        while(TRMT == 0);
        TXREG = chr + 1;

        while(RCIF == 0);
        RCIF = 0;
        chr = RCREG;
       
        Delay(50);
    }
}


割り込みを使用した場合のTX - RX ループバック・テスト
0x55 を送信して、受信したら +1 して、0x56 を送信のソフトウェア(割り込みあり)

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

#include <xc.h>

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

unsigned char chr;
unsigned int n = 0;

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

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   = 0x13;        //speed 400KHz
    SSP1STAT = 0b00000000; // 400kHz
    SSP1CON1 = 0b00101000;    //SSP1EN=1, Master mode
    SSP1CON2 = 0b00000000;

    // 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);
    }
}

void interrupt RX_int(void){
    if(RCIF == 1){
        RCIF = 0;
        chr = RCREG;
        if(RCREG != 0x55)
            return;
              
        while(TRMT == 0);
        TXREG = chr + 1;
    }
}


  1. 2021年03月10日 20:59 |
  2. PIC
  3. | トラックバック:0
  4. | コメント:0

PIC16F1825 の使い方1(32MHz クロックとポートの設定)

PIC マイコンの PIC16F1825 を使うことになったので、使い方の覚書を書いておく。
なお PIC マイコンは、3軸加速度センターのデータを I2C で取得して、それを USART で RS-422 に変換して ZYBO Z7-20 に伝送するために使用する。
今回は、32 MHz クロックの設定とポートの設定を書いておく。

PIC16F1825 は秋月電子で販売しているそのマニュアルがこちら。

また、ほぼ互換品の”PIC12(L)F1822/PIC16(L)F1823 データ シート”日本語資料が Microchip にある。

PIC のツールだが純正品の MPLAB X IDE v5.40 を使用している。
まずはクロックだが、最大の 32 MHz クロックを使用している。

#pragma config FOSC = INTOSC

OSCCON = 0b11110000; // 4x PLL enable, 8MHz or 32MHz, FOSC
OSCTUNE = 0x00;

”サヌキテックネットPICマイコン Lab.第4章 PICA Tower(ピカタワー)で学ぶ4-12.レジスターの設定”参照。

TRISA = 0;
TRISC = 0x23; // SCL, SDA, UART RX is input setting.
ANSELA = 0;
ANSELC = 0;

ポートはAポートとCポートがある。
TRISA、TRISC は入出力切り替えで 1 が入力で、0 が出力。
電子回路初心者によるPICマイコン入門 TRISAレジスタ”参照。

ANSELA、ANSELC はデジタル/アナログ設定 0 がデジタルで、1 がアナログ。
  1. 2021年03月10日 20:55 |
  2. PIC
  3. | トラックバック:0
  4. | コメント:0