Search code examples
pici2cmplabpic18

I²C Master Write with PIC18F45K50 : keeps SCL low


I'm writing my own I²C Master Write function according to Microchip's datasheet. I'm using MPLAB X. I generated the configuration with the Code Configurator, but here are the interesting bits :

// R_nW write_noTX; P stopbit_notdetected; S startbit_notdetected; BF RCinprocess_TXcomplete; SMP Standard Speed; UA dontupdate; CKE disabled; D_nA lastbyte_address; 
SSP1STAT = 0x80;
// SSPEN enabled; WCOL no_collision; CKP Idle:Low, Active:High; SSPM FOSC/4_SSPxADD_I2C; SSPOV no_overflow; 
SSP1CON1 = 0x28;
// SBCDE disabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 100ns; AHEN disabled; 
SSP1CON3 = 0x00;
// Baud Rate Generator Value: SSP1ADD 80;   
SSP1ADD = 0x50;


// clear the master interrupt flag
PIR1bits.SSP1IF = 0;
// enable the master interrupt
PIE1bits.SSP1IE = 1;

So : Standard Speed, 100ns hold time, Master Mode, clokck frequency about 50kHz.

I tried to follow the procedure described p238 of the datasheet : http://ww1.microchip.com/downloads/en/DeviceDoc/30000684B.pdf

Here's my code :

#include "mcc_generated_files/mcc.h"
#include <stdio.h>

#define _XTAL_FREQ  16000000
#define RTS_PIN     PORTDbits.RD3
#define CTS_PIN     PORTDbits.RD2
#define LED_PIN     PORTAbits.RA1
#define RX_FLAG     PORTAbits.RA2

uint8_t c;

// Define putch() for printf())
void putch(char c)
{
    EUSART1_Write(c);
}

void main(void)
{
// Initialize the device
SYSTEM_Initialize();

    while (1)
    {
        // Generate a START condition by setting Start Enable bit
        SSP1CON2bits.SEN = 1;
        // Wait for START to be completed
        while(!PIR1bits.SSPIF);
        // Clear flag
        PIR1bits.SSPIF = 0;
        // Load the address + RW byte in SSP1BUF
        // Address = 85 ; request type = WRITE (0)
        SSP1BUF = 0b10101010;
        // Wait for ack
        while (SSP1CON2bits.ACKSTAT);
        // Wait for MSSP interrupt
        while (!PIR1bits.SSPIF);
        // Load data (0x11) in SSP1BUF
        SSP1BUF = 0x11;
        // Wait for ack
        while (SSP1CON2bits.ACKSTAT);
        // Generate a STOP condition
        SSP1CON2bits.PEN = 1;
        // Wait for STOP to be completed
        while(!PIR1bits.SSPIF);
        // Clear flag
        PIR1bits.SSPIF = 0;

        // Wait for 1s before sending the next byte
        __delay_ms(1000);
    }
}

The slave device is an Arduino which I have tested with another Arduino (Master) to make sure it's working correctly.

My problem is : analysing the SDA/SCL signals with a logic analyser, when I start the PIC I get 2 correct messages, that's with correct address send and byte transmission, but at the end of the second SCL is held LOW, which makes all other writings bad (can't have a proper START condition if SCL is held LOW). BTW, at the end of the first transmission, SCL is held LOW for like 3ms, but then comes HIGH again without any reason.

Can anyone here point what I'm doing wrong ? Did I forget something ?

Thanx in advance.

Best regards.

Eric

PS : when testing the slave with another Arduino as the Master, SCL is set HIGH as soon as the transmission is over.


Solution

  • One thing I'm noticing is that after sending the slave address you are waiting for the ACK (ACKSTAT) then waiting for the SSPIF Interrupt Flag, but you are not checking for SSPIF after the data byte. You are only checking ACKSTAT. Maybe try waiting for and clearing the SSPIF before setting PEN to assert the stop conditon?

    Have you checked the state of the SSPCON and SSPSTAT registers when this behavior occurs, that might help narrow down where the problem lies.