1

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.

ricothebrol
  • 81
  • 12

2 Answers2

1

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.

K_Trenholm
  • 482
  • 5
  • 20
0

Thanx a lot for your answer !

I cleared SSP1IF after loading the data byte, and now it's working fine !

I think I understand now what was happening : the datasheet indicates that ACKSTAT is the only register bit that reacts on the rising edge of SCL, instead of the falling edge for the other bits. So in my code, I generate the STOP condition too early, and that might make it inoperative. Thus no STOP condition is generated, SCL is stuck LOW, and the next transmission cannot be started.

Furthermore, when I wait for the STOP condition to be completed, the SSP1IF flag is still set, so he doesn't actually wait and jumps directly to the delay() function. I don't know if that matters as he waits anyway, but it could matter if ever I tried to send packets one after the other.

So I here's the function I wrote, and which is working : (BTW it can take up to 255 data bytes)

void MasterWrite(char _size, char* _data)
{
    // 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);
    // Clear flag
    PIR1bits.SSPIF = 0;

    for (int i=0; i<_size; i++)
    {
        // Load data in SSP1BUF
        SSP1BUF = *(_data+i);
        // Wait for ack
        while (SSP1CON2bits.ACKSTAT);
        // Wait for MSSP interrupt
        while (!PIR1bits.SSPIF);
        // Clear flag
        PIR1bits.SSPIF = 0;
    }

    // Generate a STOP condition
    SSP1CON2bits.PEN = 1;
    // Wait for STOP to be completed
    while(!PIR1bits.SSPIF);
    // Clear flag
    PIR1bits.SSPIF = 0;
}

Thanx a lot again for your help !

Best regards.

Eric

ricothebrol
  • 81
  • 12