Totale beginner
LTE-M/NB-IoT/WiFi/BLE/GNSS - https://www.quickspot.io
Hallo,
Ik ben al de hele dag bezig om I2C werkend te krijgen op de PIC18F4550. Ik programmeer met MPLAB X en dus in C18. De 18F4550 moet als slave werken op een 100KHz bus met de raspberry pi als master. De interrupts werken maar er zijn er veel te veel, bij elke byte op de bus krijg ik een interrupt. Deze tel ik door een ledje te laten knipperen. Dit is mijn code:
unsigned int i, counter = 0;
void i2c_init(void) {
/* I2C Lines must be input according to datasheet */
TRISBbits.RB0 = 1;
TRISBbits.RB1 = 1;
/* Set up SSPADDR<7:1> contains adress */
SSPADD = I2C_ADDRESS; /* Shift bits to the left to have adress at right place */
/* Set up MSSP registers for I2C Slave communication */
SSPSTATbits.SMP = 1; /* No slew rate control */
SSPSTATbits.CKE = 0; /* No SMBus specific inputs */
SSPSTATbits.BF = 0; /* Ensure the buffer full bit is cleared */
SSPCON1bits.WCOL = 0; /* Collision bit must be cleared */
SSPCON1bits.SSPOV = 0; /* Overflow detection must be cleared */
SSPCON1bits.CKP = 1; /* Dont hold clock */
/* I2C Slave mode, 7-bit address only interrupt on adress match */
SSPCON1bits.SSPM3 = 0;
SSPCON1bits.SSPM2 = 1;
SSPCON1bits.SSPM3 = 1;
SSPCON1bits.SSPM2 = 0;
/* SSPCON2 settings */
SSPCON2bits.GCEN = 0; /* Do not respond at general calls */
SSPCON2bits.SEN = 0; /* No clock stretching */
/* Enable the MSSP module */
SSPCON1bits.SSPEN = 1;
while (1) {
for(i=0; i<counter; ++i)
{
LED = !LED;
delay_ms(200);
}
counter = 0;
}
}
void i2c_handle_data(void) {
++counter;
}De i2c_handle_data wordt opgeroepen vanuit de ISR en de SSP interrupt flag wordt ook in die externe methode gecleared en gechecked. Ik heb al 5 keer de datasheet gelezen en ik snap niet waarom ik zoveel interrupts krijg. Ook wordt er geen Ack verzonden, dus de chip lijkt geen adress match te ontvangen. Aan de zeide van de RPI werkt alles goed, dit heb ik met de LA bekeken.
Alvast bedankt,
Mvg,
Daan
Behalve het clearen van de interruptvlag moet natuurlijk ook de oorzaak van de interrupt worden bekeken en gereset, anders blijft deze voortdurend de vlag zetten en dus interrupts geven.
Ik heb een werkend voorbeeld in ASM voor een PIC18F14K50. Deze PIC wijkt v.w.b. de I2C nauwelijks af van de 4550.
In ieder geval is goed te zien dat de slave-functie duidelijk afwijkt van de master-functie.
Als je de principes van I2C kent en ASM kunt lezen, wil ik het wel in het uploadarchief zetten.
Wat in ieder geval in je software deugt, is de afhandeling van de I2C-zaken in een handler in de interruptserviceroutine. Wat natuurlijk nog telt is wat de master doet.
Totale beginner
LTE-M/NB-IoT/WiFi/BLE/GNSS - https://www.quickspot.io
Hallo,
Bedankt voor je antwoord. Assembler kan ik niet zo goed lezen. Maar ik ben ondertussen al iets verder. Ik krijg nu enkel interrupts als er een adress match is. Ik moet nu alleen nog uitvissen hoe ik in C18 het best data inlees. Want momenteel loop de controller vast bij ontvangen.
Dit is mijn code, het i²C gedeelte:
void i2c_init(void) {
/* I2C Lines must be input according to datasheet */
TRISBbits.RB0 = 1;
TRISBbits.RB1 = 1;
/* Set up regisers and enable i2c */
SSPADD = I2C_ADDRESS <<1;
SSPSTAT = 0b10000000;
SSPCON1 = 0b00110110;
SSPCON2 = 0b00000000;
}
void i2c_handle_data(void) {
unsigned char i, data = SSPBUF;
/* Only react if data in buffer is not adress data */
if(SSPSTATbits.D_A)
{
for(i=0; i<data; ++i)
{
LED = !LED;
delay_ms(200);
}
}
}En de interruptafhandeling:
#pragma code interrupt_vector = 0x08
#pragma interrupt interrupt_handler
/*
* Handle interrupts in interrupt_handler
*/
void interrupt_vector(void) {
_asm
goto interrupt_handler
_endasm
}
/*
* Initialize interrupts
*/
void interrupt_init(void)
{
/* General interrupt config */
INTCONbits.GIEH = 1; /* Enable high priority interrupts */
INTCONbits.PEIE = 1; /* Enable peripheral interrupts */
/* MSSP interrupt config */
IPR1bits.SSPIP = 1; /* Set MSSP to high priority */
PIE1bits.SSPIE = 1; /* Enable SSPIE interrupt */
}
/*
* Handle interrupts
*/
void interrupt_handler(void)
{
/* Check interrupt source and delegate */
if(INTERRUPT_I2C){
INTERRUPT_I2C = 0;
i2c_handle_data();
}
}
waarbij INTERRUPT_I2C PIR1bits.SSPIF is.
Mvg,
Daan
Het gaat erom de status van de ontvangst te onderscheiden:
;State 1: I2C master write operation, last byte was an address byte.
;SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1
;
;State 2: I2C master write operation, last byte was a data byte.
;SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1
;
;State 3: I2C master read operation, last byte was an address byte.
;SSPSTAT bits: S = 1, D_A = 0, R_W = 1
;
;State 4: I2C master read operation, last byte was a data byte.
;SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0
;
;State 5: Slave I2C logic reset by NACK from master.
;SSPSTAT bits: S = 1, D_A = 1, BF = 0
Aan de hand hiervan kun je beslissen: lezen of schrijven van de SSPBUF, het register voor inkomende en uitgaande data. In ieder geval in STATE 1 een dummy-read van deze buffer om de interruptvlag te clearen.
Totale beginner
LTE-M/NB-IoT/WiFi/BLE/GNSS - https://www.quickspot.io
@BenZ: bedankt voor het meedenken. Ik ben weer een hele stap verder.
Ik heb volgende header:
#define I2C_ADDRESS 0x20
/* I2C states */
#define I2C_MASTER_WRITE_NDA SSPSTATbits.S && !SSPSTATbits.D_A && !SSPSTATbits.R_W && SSPSTATbits.BF
#define I2C_MASTER_WRITE_DA SSPSTATbits.S && SSPSTATbits.D_A && !SSPSTATbits.R_W && SSPSTATbits.BF
#define I2C_MASTER_READ_NDA SSPSTATbits.S && !SSPSTATbits.D_A && SSPSTATbits.R_W
#define I2C_MASTER_READ_DA SSPSTATbits.S && SSPSTATbits.D_A && SSPSTATbits.R_W && !SSPSTATbits.BF
#define I2C_RESET SSPSTATbits.S && SSPSTATbits.D_A && !SSPSTATbits.BFEn volgende code
unsigned char i, data = 0;
void i2c_init(void) {
/* I2C Lines must be input according to datasheet */
TRISBbits.RB0 = 1;
TRISBbits.RB1 = 1;
/* Set up regisers and enable i2c */
SSPADD = I2C_ADDRESS <<1;
SSPSTAT = 0b10000000;
SSPCON1 = 0b00110110;
SSPCON2 = 0b00000000;
}
void i2c_handle_data(void) {
unsigned char i, data;
/* Handle data according to I2C State */
if(I2C_MASTER_WRITE_NDA)
{
/* Dummy read */
data = SSPBUF;
}
else if(I2C_MASTER_WRITE_DA)
{
/* Read data and blink LED for data times */
data = SSPBUF;
for(i=0; i<data; ++i)
{
LED = !LED;
delay_ms(200);
}
LED = 0;
}
else if(I2C_MASTER_READ_NDA)
{
}
else if(I2C_MASTER_READ_DA)
{
}
else if(I2C_RESET)
{
}
}Nu komt het rare. Als ik een 5 verstuur knippert de LED 3 keer, verstuur ik een 7 dan knippert de led 4 keer. Op de bus staan de juiste bits (5 = 101 en 7=111) dit heb ik met de LA gecontroleerd. De fout zit dus niet aan de master. Alles lijkt dus naar rechts geshift waardoor ik een bit verlies. Hoe zou dit komen.
Mvg,
Daan
Sorry, omdat het blijkbaar werkt, zij het gebrekkig, haak ik verder af. Ik heb geen zin elke regel te controleren, mede omdat ik meestal in ASM programmeer.
Totale beginner
LTE-M/NB-IoT/WiFi/BLE/GNSS - https://www.quickspot.io
Hallo,
Ik heb het probleem nog niet kunnen oplossen. Op de logic analyser zijn de bedoelde bits correct aanwezig. Echter laat de PIC eentje vallen.
Heeft iemand een idee waaraan het zou kunnen liggen?
Mvg,
Daan
Is het niet zo dat je stukje software om de led te laten knipperen als tweedeler werkt? Hij toggled namelijk.
[Bericht gewijzigd door BenZ op (11%)]
Aangezien je vanmorgen elders je project tentoonstelt, ga ik ervan uit dat de problemen zijn opgelost.
Ik zou het op prijs stellen dat dat hier ter afsluiting wordt vermeld.
Totale beginner
LTE-M/NB-IoT/WiFi/BLE/GNSS - https://www.quickspot.io
Geachte,
Ik heb nog geen tijd gehad om te antwoorden aangezien ik het vrij druk heb. Het probleem is hiermee inderdaad opgelost. Heel erg bedankt voor alle hulp en opbouwende kritiek.
Mvg,
Daan