| Electronics > Beginners |
| I2C on PIC |
| (1/4) > >> |
| Vodonik:
Hello, Im a beginner in electronics. I have experience with physics and programming. My mission, currently, is to design and implement software I2C protocol for general use. Im experimenting with GY-68 barometer with BMP180 sensor. My short term goal is to read a 'check' register from the device, which should always return 0x55. I have tried two different sets of functions i have found online, but none of them worked. I realised I could not get them to work because I had no real understanding of how I2C protocol worked at the lowest level. So I started reading and got somewhere. :) This is all of the code Im using. I expect to get 0x55, but instead I always get 0xFF. Im not asking for code that works, I want full understanding of the protocol, because Im planning to use it alot in the future. Its late where I am now, so maybe Im not making much sence. :) Also Im coding in MPLabX, using XC8 compiler and PIC16f628A chip. I will answer any questions, Im really looking forward to help in solving this. :-+ --- Code: ---#include <xc.h> // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. // CONFIG #pragma config FOSC = INTOSCCLK // Oscillator Selection bits (INTOSC oscillator: CLKOUT function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled) #pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD) #pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has PGM function, low-voltage programming enabled) #pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) #define LED1def TRISB1 #define LED1 RB1 #define LED2def TRISB2 #define LED2 RB2 #define SDA RB4 #define SDA_IN TRISB4 #define SCL RB5 #define SCL_IN TRISB5 #define _XTAL_FREQ 4000000 void delay_ms(int milliseconds) { while(milliseconds > 0) { __delay_ms(1); milliseconds--; } } void Start(void) { SDA = 1; delay_ms(1); SCL = 1; delay_ms(1); SDA = 0; delay_ms(1); SCL = 0; delay_ms(1); } void SendWriteAddress(void) { unsigned char writeAddress = 0xEE; for (char i = 0; i < 8; i++) { if (writeAddress & 0x80) SDA = 1; else SDA = 0; delay_ms(1); SCL = 1; delay_ms(1); SCL = 0; delay_ms(1); writeAddress <<= 1; } } void SendRegisterAddress(void) { unsigned char registerAddress = 0xD0; for (char i = 0; i < 8; i++) { if (registerAddress & 0x80) SDA = 1; else SDA = 0; delay_ms(1); SCL = 1; delay_ms(1); SCL = 0; delay_ms(1); registerAddress <<= 1; } } void SendReadAddress(void) { unsigned char readAddress = 0xD0; for (char i = 0; i < 8; i++) { if (readAddress & 0x80) SDA = 1; else SDA = 0; delay_ms(1); SCL = 1; delay_ms(1); SCL = 0; delay_ms(1); readAddress <<= 1; } } unsigned char RecieveByte(void) { unsigned char recievedData = 0x00; char binaryData[8] = {0}; SDA = 0; SDA_IN = 1; delay_ms(10); //Just in case. for (char i = 0; i < 8; i++) { SCL = 1; while (!SCL) { delay_ms(1); } binaryData[i] = SDA; delay_ms(1); SCL = 0; } delay_ms(10); SDA_IN = 0; //Convert binary to hex: for (int i = 0; i < 8; i++) { recievedData <<= 1; if (binaryData[i] == 1) recievedData |= 1; } return recievedData; } void Stop(void) { SCL = 1; delay_ms(1); SDA = 1; } void main(void) { LED1def = 0; LED1 = 0; LED2def = 0; LED2 = 0; SDA_IN = 0; SCL_IN = 0; SDA = 1; SCL = 1; delay_ms(10); unsigned char testVariable = 0xCC; Start(); SendWriteAddress(); SendRegisterAddress(); Start(); SendReadAddress(); testVariable = RecieveByte(); Stop(); while(1) { for (char i = 0; i < 8; i++) { if (testVariable & 0x80) { LED1 = 1; delay_ms(100); LED1 = 0; delay_ms(900); } else { LED2 = 1; delay_ms(100); LED2 = 0; delay_ms(900); } testVariable <<= 1; } delay_ms(2000); } } --- End code --- |
| MarkF:
May I ask why you want to do this is software? The PIC has hardware I2C and it would perform faster and better. I have only used the SPI protocol and don't have experience with the I2C protocol. Although I'm interesting in trying I2C. May I suggest you spend a lot of time looking over the timing diagrams and the I2C description in the specification for the PIC you're using. From a few problems selecting the correct SPI mode for some devices, your scope and the timing diagrams will be your best friend. I'm a EE with 35 years of software development. Don't worry about size. Just code it as simple and straight forward as possible. Test with your scope as you go (especially since you're trying to do the timing in software). Also double check your data is packed/unpacked correctly and that the bit order to/from the hardware is correct (MSB or LSB is first). Good luck |
| Maxlor:
In SendReadAddress, you use the wrong address: you wrote the register address there (0xD0), probably because you copy/pasted, but you should be using the device address with read bit set (0xEF). I have to agree with MarkF here, the PICs have I2C hardware, which is easier to use and more robust than bit-banging, even though you seem to be doing a good job, taking into account clock stretching and so on. And using the peripheral, it runs much faster. All the delays can cause problems, because there are I2C slaves that reset if no valid data is seen on the bus for a while (one device I've worked with had a reset after 20ms.) The BMP180 isn't one of them though, I think. |
| Maxlor:
Here's an example of how to use the I2C peripheral on a PIC. This was written for an PIC16F1822, some details (like available status bits) might be different with the chip you use, but the general concept should be the same. The function supports write-restart-read, which is what you seem to need. The crucial thing to note is that the PIC notices when you write SSP1BUF, and starts sending automatically. It tells you when it's done by setting status bits, so in the code I wait for those bits to be set using the various while loops. If busy-looping isn't your thing, you could also configure interrupts, so you can do other stuff while the chip is communicating. You should never use fixed delays in code like this (with a few exception, like when a datasheet tells you that you need to wait 10ms after startup, or 750ms after sending a "measure" command before reading the result), because that isn't fast, reliable nor maintainable. --- Code: ---void i2c_init() { // Configure RA1/SCL and RA2/SDA as digital inputs TRISAbits.TRISA1 = 1; TRISAbits.TRISA2 = 1; ANSELAbits.ANSA1 = 0; ANSELAbits.ANSA2 = 0; // configure MSSP module for I2C SSP1STAT = 0b10000000; // disable slew rate control and SMBus compatibility SSP1CON1 = 0b00111000; // enable and configure module for I2C SSP1ADD = 0; // should result in 100kHz I2C clock with 500kHz oscillator } uint8_t i2c_writeRead(uint8_t address, uint8_t *wData, uint8_t wLen, uint8_t *rData, uint8_t rLen) { if ((wLen | rLen) == 0) { return 1; } uint8_t success = 0; address <<= 1; if (wLen) { SSP1CON2bits.SEN = 1; // send start while (SSP1STATbits.S); SSP1BUF = address; // address and write bit while (SSP1STATbits.BF); if (SSP1CON2bits.ACKSTAT) { goto end; } do { SSP1BUF = *wData; ++wData; --wLen; while (SSP1STATbits.BF); if (SSP1CON2bits.ACKSTAT) { goto end; } } while (wLen); } if (rLen) { if (wData) { SSP1CON2bits.RSEN = 1; // send restart while (SSP1CON2bits.RSEN); } else { SSP1CON2bits.SEN = 1; // send start while (SSP1STATbits.S); } SSP1BUF = address | 1; // address and read bit while (SSP1STATbits.BF); if (SSP1CON2bits.ACKSTAT) { goto end; } do { SSP1CON2bits.RCEN = 1; // receive while (SSP1CON2bits.RCEN); *rData++ = SSP1BUF; --rLen; // send ACK (or NACK for last byte) if (rLen) { SSP1CON2bits.ACKDT = 0; } else { SSP1CON2bits.ACKDT = 1; } SSP1CON2bits.ACKEN = 1; while (SSP1CON2bits.ACKEN); } while (rLen); } success = 1; end: SSP1CON2bits.PEN = 1; while (SSP1CON2bits.PEN); return success; } --- End code --- |
| Vodonik:
Hi guys! The main reason im using bit-banging, my favorite phrase from now on :), is my PIC does not support hardware I2C. Do you have any idea why my code is not working? The address in SendReadAddress function address is only wrong on forum, in my project is ok, and still I only get all ones. I guess its because slave is not responding (not pulling SDA to ground). But I have no idea why or an oscilloscope, so I cant even guess. Or some reading material for this kind of situation? :) |
| Navigation |
| Message Index |
| Next page |