Tôi đã đăng bài này một vài ngày trước trên Diễn đàn Microchip (here) nhưng câu trả lời duy nhất là dế. Mã I2C bên dưới hoạt động phần lớn thời gian nhưng đôi khi bật nguồn có một va chạm xe buýt (BCLIF) và mô-đun I2C không thể phục hồi sau BCLIF. Các dòng I2C được kéo lên bởi 3,3K ohms. Sử dụng REALICE và breakpoints Tôi có thể thấy rằng i2c_write()
đặt lại BCLIF và trả về FALSE khi BCLIF được đặt. Tôi đã sử dụng phạm vi để xác minh rằng xe buýt I2C có lót bằng phẳng. Khởi tạo lại mô-đun I2C PIC18F25K20 (xem init_i2c()
bên dưới) khi i2c_write()
trả về FALSE không hiệu quả. PIC18F25K20 I2C được kết nối với một thiết bị nô lệ đơn (MCP4018 I2C Digital POT). Tôi đã sử dụng mã này trên các dự án PIC18 trước đó mà không có vấn đề vì vậy tôi thay thế MCP4018 nghi ngờ một phần xấu nhưng thấy không có sự khác biệt. Có cách nào để đặt lại mô-đun I2C PIC18F25K20 khi nó bị khóa không?Làm thế nào để phục hồi từ vụ va chạm xe buýt I2C BCLIF?
void init_i2c(I2C_BAUD_RATE baud_rate, float freq_mhz)
{
UINT32 freq_cycle;
/* Reset i2c */
SSPCON1 = 0;
SSPCON2 = 0;
PIR2bits.BCLIF = 0;
/* Set baud rate */
/* SSPADD = ((Fosc/4)/Fscl) - 1 */
freq_cycle = (UINT32) ((freq_mhz * 1e6)/4.0);
if (baud_rate == I2C_1_MHZ)
{
SSPADD = (UINT8) ((freq_cycle/1000000L) - 1);
SSPSTATbits.SMP = 1; /* disable slew rate for 1MHz operation */
}
else if (baud_rate == I2C_400_KHZ)
{
SSPADD = (UINT8) ((freq_cycle/400000L) - 1);
SSPSTATbits.SMP = 0; /* enable slew rate for 400kHz operation */
}
else /* default to 100 kHz case */
{
SSPADD = (UINT8) ((freq_cycle/100000L) - 1);
SSPSTATbits.SMP = 1; /* disable slew rate for 1MHz operation */
}
/* Set to Master Mode */
SSPCON1bits.SSPM3 = 1;
SSPCON1bits.SSPM2 = 0;
SSPCON1bits.SSPM1 = 0;
SSPCON1bits.SSPM0 = 0;
/* Enable i2c */
SSPCON1bits.SSPEN = 1;
}
BOOL i2c_write(UINT8 addr, const void *reg, UINT16 reg_size, const void *data, UINT16 data_size)
{
UINT16 i;
const UINT8 *data_ptr, *reg_ptr;
/* convert void ptr to UINT8 ptr */
reg_ptr = (const UINT8 *) reg;
data_ptr = (const UINT8 *) data;
/* check to make sure i2c bus is idle */
while ((SSPCON2 & 0x1F) | (SSPSTATbits.R_W))
;
/* initiate Start condition and wait until it's done */
SSPCON2bits.SEN = 1;
while (SSPCON2bits.SEN)
;
/* check for bus collision */
if (PIR2bits.BCLIF)
{
PIR2bits.BCLIF = 0;
return(FALSE);
}
/* format address with write bit (clear last bit to indicate write) */
addr <<= 1;
addr &= 0xFE;
/* send out address */
if (!write_byte(addr))
return(FALSE);
/* send out register/cmd bytes */
for (i = 0; i < reg_size; i++)
{
if (!write_byte(reg_ptr))
return(FALSE);
}
/* send out data bytes */
for (i = 0; i < data_size; i++)
{
if (!write_byte(data_ptr))
return(FALSE);
}
/* initiate Stop condition and wait until it's done */
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN)
;
/* check for bus collision */
if (PIR2bits.BCLIF)
{
PIR2bits.BCLIF = 0;
return(FALSE);
}
return(TRUE);
}
BOOL write_byte(UINT8 byte)
{
/* send out byte */
SSPBUF = byte;
if (SSPCON1bits.WCOL) /* check for collision */
{
return(FALSE);
}
else
{
while(SSPSTATbits.BF) /* wait for byte to be shifted out */
;
}
/* check to make sure i2c bus is idle before continuing */
while ((SSPCON2 & 0x1F) | (SSPSTATbits.R_W))
;
/* check to make sure received ACK */
if (SSPCON2bits.ACKSTAT)
return(FALSE);
return(TRUE);
}
Tôi không thể thấy mã nơi các chân SDA và SCL được định cấu hình làm đầu vào. –
Bạn có thể thử trao đổi điều kiện bắt đầu và kiểm tra va chạm bus trong i2c_write() và thực thi hàm này vài lần liên tiếp với một số sự chậm trễ nhỏ giữa khi vấn đề này xảy ra? Chuyện gì xảy ra sau đó? –
Ngoài ra, nó có thể thực sự tiện dụng nếu bạn có dao động với tự động kích hoạt, vì vậy bạn có thể theo dõi hành vi của dòng SDA sau khi bật nguồn là gì. Trạng thái Datasheet: * Nếu ở đầu điều kiện Khởi động, các chân SDA và SCL đã được lấy mẫu thấp hoặc nếu trong điều kiện Khởi động, dòng SCL được lấy mẫu thấp trước khi đường SDA được điều khiển xuống thấp, xảy ra va chạm trên xe buýt, Bus Interrupt Flag Flag, BCLIF, được thiết lập, điều kiện Start bị hủy bỏ và module I2C được reset về trạng thái Idle *. –