Först en "dum fråga": När man pratar om hastigheten på i2c bussen ex 100kHz, menar man då frekvensen på klockan? Alltså om jag tar 1/periodtiden på klockan så får jag hastigheten?
I så fall så har jag ett följdproblem: Jag vill få upp hastigheten på bussen till 100kHz
När jag mäter på min buss med en logikanalysator så får jag cirka 20 kHz.
Jag försöker kommunicera med en kompass (hmc6352) och en PIC18F4620 med intern oscillator på 8MHz.
Jag använder Hi-Techs kompilator och deras bibliotek för i2c.
Jag bifogar koden jag använder:
Först och främst i2c.h
Här har jag ändrat (enligt databladet) så att MASTER_MODE = 0b1000 // I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
Sedan sätter jag SSPADD = 19; i main vilket borde ge mig 100kHz eftersom (8*10^6) / (100*10^3) = 4 * (SSPADD +1) <=> SSPADD = (80 / 4) - 1 = 19
Kod: Markera allt
#ifndef _I2C_H_
#define _I2C_H_
/*
* SDA (data) and SCL (clock) bits
*
* Special note!!!
*
* If the clock and data lines are in the same port, you will need
* to beware of the Read/Modify/Write issue in the PIC - since
* a bit set or clear on any one bit in a port will read and write
* back all other bits, any bits configured as input which
*/
/* Uncomment the next line to use the PIC's SSP Module*/
#define I2C_MODULE 1
#ifdef I2C_MODULE
/* I2C module uses PORT C */
#define SCL RC3 /* clock on port C bit 2 */
#define SCL_DIR TRISC3
#define SDA RC4 /* data on port C bit 1 */
#define SDA_DIR TRISC4
#define I2CTRIS TRISC
//#define MASTER_MODE 0B1011 /* I2C firmware controlled Master Mode (slave idle) */
#define MASTER_MODE 0B1000 /* I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) */
#define SSPMode(val) SSPCON1 &=0xF0; SSPCON1 |=(val & 0xf)
#else
/* Change port as required - defaults to port b */
#define SCL RB2 /* clock on port B bit 2 */
#define SCL_DIR TRISB2
#define SDA RB1 /* data on port B bit 1 */
#define SDA_DIR TRISB1
#define I2CTRIS TRISB
#endif
#define M_SDA_INP 0x02
#define M_SDA_OUT 0xFD
#define M_SCL_INP 0x04
#define M_SCL_OUT 0xFB
#define I2C_INPUT 1 /* data direction input */
#define I2C_OUTPUT 0 /* data direction output */
#define I2C_READ 0x01 /* read bit used with address */
#define I2C_WRITE 0x00 /* write bit used with address */
#define FALSE 0
#define TRUE !FALSE
#define I2C_ERROR (-1)
#define I2C_LAST FALSE /* SendAck: no more bytes to send */
#define I2C_MORE TRUE /* SendAck: more bytes to send */
#define i2c_Start() i2c_Restart()
#define i2c_WriteTo(address) i2c_Open((address), I2C_WRITE)
#define i2c_ReadFrom(address) i2c_Open((address), I2C_READ)
#ifdef I2C_MODULE
#define SCL_HIGH() SCL_DIR = I2C_INPUT
#define SCL_LOW() SCL_DIR = I2C_OUTPUT
#define SDA_HIGH() SDA_DIR = I2C_INPUT
#define SDA_LOW() SDA_DIR = I2C_OUTPUT
#else
#define SCL_HIGH() SCL = 1; SCL_DIR = I2C_OUTPUT
#define SCL_LOW() SCL = 0; SCL_DIR = I2C_OUTPUT
#define SDA_HIGH() SDA = 1; SDA_DIR = I2C_OUTPUT
#define SDA_LOW() SDA = 0; SDA_DIR = I2C_OUTPUT
#endif
/*
* Timings for the i2c bus. Times are rounded up to the nearest
* micro second.
*/
#define I2C_TM_BUS_FREE 5
#define I2C_TM_START_SU 5
#define I2C_TM_START_HD 4
#define I2C_TM_SCL_LOW 5
#define I2C_TM_SCL_HIGH 4
#define I2C_TM_DATA_SU 1
#define I2C_TM_DATA_HD 0
#define I2C_TM_SCL_TO_DATA 4 /* SCL low to data valid */
#define I2C_TM_STOP_SU 4
//#define I2C_TM_SCL_TMO 10 /* clock time out */
#define I2C_TM_SCL_TMO 10 /* clock time out */
extern signed char i2c_ReadAcknowledge(void);
extern unsigned char i2c_SendAddress(unsigned char, unsigned char);
extern unsigned char i2c_SendByte(unsigned char);
extern int i2c_ReadByte(void);
extern void i2c_Restart(void);
extern void i2c_Stop(void);
extern void i2c_SendAcknowledge(unsigned char);
extern signed char i2c_PutByte(unsigned char);
extern int i2c_GetByte(unsigned char);
extern unsigned char i2c_Open(unsigned char, unsigned char);
extern unsigned char i2c_GetString(unsigned char *, unsigned char);
extern int i2c_PutString(const unsigned char *, unsigned char);
extern unsigned char i2c_WaitForSCL(void);
extern void i2c_Free();
#endif /* _I2C_H_ */
Kod: Markera allt
#include <pic18.h>
#include "delay.h"
#include "i2c.h"
/*
* I2C functions for HI-TECH PIC C - master mode only
*/
/*
* TIMING - see Philips document: THE I2C-BUS SPECIFICATION
*/
/*
* Send stop condition
* - data low-high while clock high
*/
void
i2c_Stop(void)
{
/* don't assume SCL is high on entry */
/*------------------------------------------------------------------------------------*/
//SDA_LOW(); /* ensure data is low first */
//SCL_HIGH();
/*------------------------------------------------------------------------------------*/
SCL_LOW(); // Use this line for START
SDA_LOW(); /* ensure data is low first */
DelayUs(I2C_TM_DATA_SU);
SCL_DIR = I2C_INPUT; /* float clock high */
DelayUs(I2C_TM_STOP_SU);
SDA_HIGH(); /* the low->high data transistion */
DelayUs(I2C_TM_BUS_FREE); /* bus free time before next start */
SDA_DIR = I2C_INPUT; /* float data high */
return;
}
/*
* Send (re)start condition
* - ensure data is high then issue a start condition
* - see also i2c_Start() macro
*/
void
i2c_Restart(void)
{
SCL_LOW(); /* ensure clock is low */
SDA_HIGH(); /* ensure data is high */
DelayUs(I2C_TM_DATA_SU);
SCL_DIR = I2C_INPUT; /* clock pulse high */
DelayUs(I2C_TM_SCL_HIGH);
SDA_LOW(); /* the high->low transition */
DelayUs(I2C_TM_START_HD);
return;
}
/*
* Send a byte to the slave
* - returns true on error
*/
unsigned char
i2c_SendByte(unsigned char byte)
{
signed char i;
for(i=7; i>=0; i--)
{
SCL_LOW(); /* drive clock low */
/* data hold time = 0, send data now */
SDA_DIR = ((byte>>i)&0x01);
if ((byte>>i)&0x01) { /* bit to send */
SDA_HIGH();
}
else {
SDA_LOW();
}
DelayUs(I2C_TM_DATA_SU);
SCL_DIR = I2C_INPUT; /* float clock high */
if(i2c_WaitForSCL()) /* wait for clock release */
return TRUE; /* bus error */
DelayUs(I2C_TM_SCL_HIGH); /* clock high time */
}
return FALSE;
}
/*
* send an address and data direction to the slave
* - 7-bit address (lsb ignored)
* - direction (FALSE = write )
*/
unsigned char
i2c_SendAddress(unsigned char address, unsigned char rw)
{
return i2c_SendByte(address | (rw?1:0));
}
/*
* Check for an acknowledge
* - returns ack or ~ack, or ERROR if a bus error
*/
signed char
i2c_ReadAcknowledge(void)
{
unsigned char ack;
SCL_LOW(); /* make clock is low */
SDA_DIR = I2C_INPUT; /* disable data line - listen for ack */
DelayUs(I2C_TM_SCL_TO_DATA); /* SCL low to data out valid */
SCL_DIR = I2C_INPUT; /* float clock high */
DelayUs(I2C_TM_DATA_SU);
ack = SDA; /* read the acknowledge */
/* wait for slave to release clock line after processing byte */
if(i2c_WaitForSCL())
return I2C_ERROR;
return ack;
}
/*
* Read a byte from the slave
* - returns the byte, or I2C_ERROR if a bus error
*/
int
i2c_ReadByte(void)
{
unsigned char i;
unsigned char byte = 0;
for(i=0; i<8; i++)
{
SCL_LOW(); /* drive clock low */
DelayUs(I2C_TM_SCL_LOW); /* min clock low period */
SDA_DIR = I2C_INPUT; /* release data line */
SCL_DIR = I2C_INPUT; /* float clock high */
if(i2c_WaitForSCL())
return I2C_ERROR;
DelayUs(I2C_TM_SCL_HIGH);
byte = byte << 1; /* read the next bit */
byte |= SDA;
}
return (int)byte;
}
/*
* Send an (~)acknowledge to the slave
* - status of I2C_LAST implies this is the last byte to be sent
*/
void
i2c_SendAcknowledge(unsigned char status)
{
SCL_LOW();
if ( status & 0x01) {
SDA_LOW(); /* drive line low -> more to come */
}
else {
SDA_HIGH();
}
DelayUs(I2C_TM_DATA_SU);
SCL_DIR = I2C_INPUT; /* float clock high */
DelayUs(I2C_TM_SCL_HIGH);
return;
}
/*
* Send a byte to the slave and acknowledges the transfer
* - returns I2C_ERROR, ack or ~ack
*/
signed char
i2c_PutByte(unsigned char data)
{
if(i2c_SendByte(data))
return I2C_ERROR;
return i2c_ReadAcknowledge(); /* returns ack, ~ack */
}
/*
* Get a byte from the slave and acknowledges the transfer
* - returns true on I2C_ERROR or byte
*/
int
i2c_GetByte(unsigned char more)
{
int byte;
if((byte = i2c_ReadByte()) == I2C_ERROR)
return I2C_ERROR;
i2c_SendAcknowledge(more);
return byte;
}
/*
* Send an array of bytes to the slave and acknowledges the transfer
* - returns number of bytes not successfully transmitted
*/
int
i2c_PutString(const unsigned char *str, unsigned char length)
{
signed char error;
while(length)
{
if((error = i2c_PutByte(*str)) == I2C_ERROR)
return -(int)length; /* bus error */
else
if(error)
return (int)length; /* non acknowledge */
str++;
length--;
}
return FALSE; /* everything OK */
}
/*
* Reads number bytes from the slave, stores them at str and acknowledges the transfer
* - returns number of bytes not successfully read in
*/
unsigned char
i2c_GetString(unsigned char *str, unsigned char number)
{
int byte;
while(number)
{
if((byte = i2c_GetByte(number-1)) == I2C_ERROR)
return number; /* bus error */
else
*str = (unsigned char)byte;
str++;
number--;
}
return FALSE; /* everything OK */
}
/*
* Opens communication with a device at address. mode
* indicates I2C_READ or I2C_WRITE.
* - returns TRUE if address is not acknowledged
*/
unsigned char
i2c_Open(unsigned char address, unsigned char mode)
{
i2c_Start();
i2c_SendAddress(address, mode);
if(i2c_ReadAcknowledge())
return TRUE;
return FALSE;
}
/*
* wait for the clock line to be released by slow slaves
* - returns TRUE if SCL was not released after the
* time out period.
* - returns FALSE if and when SCL released
*/
unsigned char
i2c_WaitForSCL(void)
{
/* SCL_DIR should be input here */
if(!SCL)
{
DelayUs(I2C_TM_SCL_TMO);
/* if the clock is still low -> bus error */
if(!SCL)
return TRUE;
}
return FALSE;
}
void
i2c_Free()
{
unsigned char ucI;
SDA_DIR=I2C_INPUT;
for(ucI=0;ucI!=9;ucI++)
{
SCL_HIGH();
DelayUs(5);
SCL_LOW();
DelayUs(5);
}
}
unsigned char i2c_read(unsigned char ucAdr)
{
unsigned char ucDat;
if (i2c_ReadFrom(ucAdr)==0)
{
ucDat=i2c_GetByte(I2C_MORE);
i2c_Stop();
}
return(ucDat);
}
Kod: Markera allt
/* I2C test program that writes & reads data to an I2C EEPROM device. */
#include <pic18.h>
#include "delay.h"
#include "i2c.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define BAUD 4800
#define FOSC 8000000L
//#define DIVIDER ((int)(FOSC/(16UL * BAUD) -1))// for >= 9600 baud
#define DIVIDER ((int)(FOSC/(64UL * BAUD) -1))// for < 9600 baud
#define ROM 0xA0 /* I2C EEPROM */
unsigned char buffer[5]= 0;
void i2c_eeprom_write_byte( int , unsigned int , char, int );
void i2c_eeprom_write_string( int , unsigned int , char[]);
void usart(void){
TRISC = 0XFF;
TXEN = 1; //Allows sending
SYNC = 0; //The SYNC bit should be cleared to select asynchronous operation.
SPEN = 1; //SPEN bit is used to enable the entire serial port
BRGH = 0;
SPBRG = DIVIDER; // 4800
BRG16 = 0;
TX9 = 0;
RX9 = 0;
CREN = 0;
CREN = 1; //Allows receiving
GIE = 0; //*Interrupts
PEIE = 0;
TXIE = 0; //Disable transmit interrupt
RCIE = 0; //Disable receive interrupt
}
void putch(unsigned char c){
while(!TXIF) //Set when TXREG is empty
continue; //Continue looping while TXREG is not available
TXREG = c; //Put character to be sent onto TXREG
}
void setup(void){
OSCCON = 0b01110000; // internal osc select, 8MHz
ADCON1 = 0xFF; // All pins digital
usart();
}
void main(void) {
unsigned char count,val;
setup();
TRISD=0; /* use a led on RB0 - set as output */
PORTD=0;
RD1=0;
char data = 0;
char dataString[5];
/* initialize i2c */
#ifdef I2C_MODULE
SSPMode(MASTER_MODE);
SSPEN = 1;
CKP = 1;
#else
SCL_DIR = I2C_OUTPUT;
SDA_DIR = I2C_OUTPUT;
SDA = 0;
SCL = 0;
#endif
SSPADD = 19;
int var = 0;
char data;
DelayMs(100);
DelayMs(100);
DelayMs(100);
DelayMs(100);
DelayMs(100);
DelayMs(100);
char data = 0xFF;
i2c_WriteTo(0x42);
i2c_PutByte('A');
i2c_Stop();
DelayMs(3);
i2c_ReadFrom(0x43);
i2c_Start();
data = i2c_GetByte(1);
ACKDT=0;
ACKEN=1;
data = i2c_GetByte(0);
i2c_Stop();
while(1) {
}
}
Är det något annat jag behöver tänka på om jag ska ändra hastigheten?