I²C Master Write with PIC18F45K50 :保持 SCL 为低电平

I²C Master Write with PIC18F45K50 : keeps SCL low

我正在根据 Microchip 的数据表编写自己的 I²C Master Write 函数。我正在使用 MPLAB X。我使用代码配置器生成了配置,但这里有一些有趣的部分:

// 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;

所以:标准速度,100ns 保持时间,主控模式,时钟频率约 50kHz。

我尝试按照数据表 p238 中描述的程序进行操作: http://ww1.microchip.com/downloads/en/DeviceDoc/30000684B.pdf

这是我的代码:

#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);
    }
}

从设备是一个 Arduino,我已经用另一个 Arduino(Master)测试过它以确保它工作正常。

我的问题是:用逻辑分析仪分析 SDA/SCL 信号,当我启动 PIC 时,我得到 2 条正确的消息,即正确的地址发送和字节传输,但在第二个 SCL 的末尾保持低电平,这会使所有其他写入变坏(如果 SCL 保持低电平,则无法获得正确的启动条件)。顺便说一句,在第一次传输结束时,SCL 保持低电平大约 3 毫秒,但随后无故再次变为高电平。

这里有人能指出我做错了什么吗?我是不是忘记了什么?

提前致谢。

此致。

埃里克

PS : 当用另一个Arduino作为Master测试slave时,SCL在传输结束后立即设置为HIGH。

我注意到的一件事是,在发送从属地址后,您正在等待 ACK (ACKSTAT),然后等待 SSPIF 中断标志​​,但您没有在数据字节后检查 SSPIF。您只检查 ACKSTAT。也许在设置 PEN 断言停止条件之前尝试等待并清除 SSPIF?

发生这种情况时,您是否检查过 SSPCON 和 SSPSTAT 寄存器的状态,这可能有助于缩小问题所在的范围。

非常感谢您的回答!

我在加载数据字节后清除了 SSP1IF,现在它工作正常!

我想我现在明白发生了什么:数据表表明 ACKSTAT 是唯一在 SCL 的上升沿而不是其他位的下降沿做出反应的寄存器位。所以在我的代码中,我过早地生成了 STOP 条件,这可能会使其无法运行。因此没有生成停止条件,SCL 卡在低电平,下一次传输无法开始。

另外,我在等待STOP条件完成的时候,SSP1IF标志还是置位的,所以他实际上并没有等待,直接跳转到了delay()函数。我不知道这在他等待时是否重要,但如果我尝试一个接一个地发送数据包,这可能很重要。

所以这是我编写的函数,它正在运行: (顺便说一句,它最多可以占用 255 个数据字节)

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;
}

再次感谢您的帮助!

此致。

埃里克