Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

i2c на efr32bg24

Сб ноя 19, 2022 11:36:08

У меня проблема с написанием драйвера мастера i2c на вышепомянутом кристалле. Хочу сделать по прерываниям, а не по опросу. Вроде всё нормально работало, в пределах необходимого мне. Но тут понадобилось чуть больше и всплыла проблема. Не получается работать с EEPROM которой надо передавать 16 битный адрес регистра (типа 24c512). С меньшими у меня всё, вроде, работало нормально. А тут я получаю NAK.

Вот по отладочной информации хронология возникновения прерываний:

Принцип такой. Из функции i2c_rd_reg16 я инициализирую глобальные переменные (состояние = send_rcv_hi) и посылаю команду СТАРТ. всё остальное происходит в событиях по прерыванию.
Прерывание СТАРТ: посылаем адрес устройства 0xA8.
Прерывание буфер передачи пуст: Посылаем старший байт адреса регистра и меняем состояние на send_rcv_lo
Прерывание буфер передачи пуст: Посылаем младший байт адреса регистра и меняем состояние на rcv_restart

Вот тут возникает проблема. Так как буфер передачи имеет двухуровневый FIFO ACK начинают приходить только сейчас:
Прерывание ACK: видя состояние rcv_restart посылается команда старт и меняется состяние на rcv_data. Хотя этот ACK на переданный адрес 0xA8.
Прерывание ACK: ничего не делаем (это на первый байт адреса регистра)

Прерывание СТАРТ: посылаем адрес устройства 0xA9.
Прерывание ACK: ничего не делаем (а вот это на второй байт адреса регистра?)
Прерывание NAK - облом.

Т.е. проблема в том, что рестарт происходит не после передачи 2 байта адреса регистра, а раньше. В результате чего этот второй байт воспринимается как i2c адрес и на него никто не отзывается. Логического анализатора у меня нет, но я догадываюсь. Если пытаться из EEPROM читать по адресу xxA9 - NAK не приходит (и читает откуда-то из другого места). Т.е. выходит, что второй байт передаётся после рестарта.

Есть идеи как сделать правильно?

Спойлер
Код:
#include "em_device.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "i2c_drv.h"
#include "resources.h"

#define I2C_DEV_NUM 1
#define I2C_DEV     I2C1
#define I2C_IRQn    I2C1_IRQn
#define I2C_IRQHandler  I2C1_IRQHandler
#define CMU_CLOCK   cmuClock_I2C1


#define SDA_PORT    gpioPortC
#define SCL_PORT    gpioPortC
#define SDA_PIN         (1)
#define SCL_PIN         (2)

typedef enum {
    send_reg_hi,
    send_reg_lo,
    send_data,

    rcv_reg_hi,
    rcv_reg_lo,
    rcv_restart,
    rcv_data,
    rcv_stop,

    i2c_stop,
    i2c_idle,
} i2c_state_t;

volatile i2c_state_t i2c_state = i2c_idle;
volatile unsigned int data_count, i2c_register[2], i2c_addr;
unsigned char * volatile data_ptr;
volatile t_i2c_status i2c_error_code;

#define DEBUG_I2C

#ifdef DEBUG_I2C
volatile uint8_t debug_str[256], *debug_str_ptr = 0;
uint8_t hex_str[] = "0123456789ABCDEF";
#endif

void i2c_master_init(void) {
  // включаем внутренние подтяжки на всякий случай.
  // И тогда в некоторых случаях можно обойтись и без внешних резисторов.
  CMU_ClockEnable(CMU_CLOCK, 1);

  GPIO_PinModeSet(SDA_PORT, SDA_PIN, gpioModeWiredAndPullUp, 1);
  GPIO_PinModeSet(SCL_PORT, SCL_PIN, gpioModeWiredAndPullUp, 1);


  // Конфигурируем
  I2C_DEV->CTRL = I2C_CTRL_GIBITO | I2C_CTRL_BITO_I2C160PCC; // | I2C_CTRL_TXBIL_HALF_FULL; // | I2C_CTRL_AUTOSN;
  {
    unsigned int refFreq, clkdiv;
    const unsigned int baudrate = 400000;
    refFreq = CMU_ClockFreqGet(cmuClock_PCLK);
    clkdiv  = refFreq / baudrate;
    clkdiv = (clkdiv - 8 +4 ) / 8 ;
    I2C_DEV->CLKDIV = clkdiv-1;
  }

  I2C_DEV->IEN = I2C_IEN_ARBLOST | I2C_IEN_NACK | I2C_IEN_RXDATAV |
      I2C_IEN_START | I2C_IEN_MSTOP | I2C_IEN_ACK;

  // подключаем выводы
  GPIO->I2CROUTE[I2C_DEV_NUM].SCLROUTE = SCL_PORT | (SCL_PIN << _GPIO_I2C_SCLROUTE_PIN_SHIFT);
  GPIO->I2CROUTE[I2C_DEV_NUM].SDAROUTE = SDA_PORT | (SDA_PIN << _GPIO_I2C_SDAROUTE_PIN_SHIFT);
  GPIO->I2CROUTE[I2C_DEV_NUM].ROUTEEN = GPIO_I2C_ROUTEEN_SCLPEN | GPIO_I2C_ROUTEEN_SDAPEN;

  I2C_DEV->EN = I2C_EN_EN;
  I2C_DEV->CMD = I2C_CMD_ABORT | I2C_CMD_CLEARTX | I2C_CMD_CLEARPC;

  NVIC_SetPriority(I2C_IRQn, I2C_IRQ_PRI);
  NVIC_EnableIRQ(I2C_IRQn);

}

void I2C_IRQHandler(void) {

  if ((I2C_DEV->IEN & I2C_IEN_ARBLOST) && (I2C_DEV->IF & I2C_IF_ARBLOST)) {// Arbitration lost; Interrupt Flag: UCALIFG; Interrupt
#ifdef DEBUG_I2C
      if (debug_str_ptr) *debug_str_ptr++ = 'A';
#endif
      i2c_error_code = I2C_ERROR;
      i2c_state = i2c_idle;
      I2C_DEV->IF_CLR = I2C_IF_ARBLOST;
  }
  if ((I2C_DEV->IEN & I2C_IEN_START) && (I2C_DEV->IF & I2C_IF_START)) { // Start condition received; Interrupt Flag: UCSTTIFG
#ifdef DEBUG_I2C
      if (debug_str_ptr) *debug_str_ptr++ = 'S';
      if (debug_str_ptr) *debug_str_ptr++ = hex_str[(i2c_addr & 0xF0) >> 4];
      if (debug_str_ptr) *debug_str_ptr++ = hex_str[(i2c_addr & 0x0F)];
#endif
      I2C_DEV->TXDATA = i2c_addr;
      I2C_DEV->IF_CLR = I2C_IF_START;
      if (i2c_state != rcv_data) {
          BUS_RegMaskedSet(&I2C_DEV->IEN, I2C_IEN_TXBL);
      }
  }
  if ((I2C_DEV->IEN & I2C_IEN_RXDATAV) && (I2C_DEV->IF & I2C_IF_RXDATAV)) { // Data received; Interrupt Flag: UCRXIFG0
#ifdef DEBUG_I2C
      if (debug_str_ptr) *debug_str_ptr++ = 'R';
#endif
      *data_ptr++ = I2C_DEV->RXDATA;
      I2C_DEV->IF_CLR= I2C_IF_RXDATAV;
      if (--data_count) {
          I2C_DEV->CMD = I2C_CMD_ACK;
      } else {
          i2c_state = rcv_stop;
          I2C_DEV->CMD = I2C_CMD_NACK | I2C_CMD_STOP;
      }
  }


  if ((I2C_DEV->IEN & I2C_IEN_TXBL) && (I2C_DEV->IF & I2C_IF_TXBL)) { // Transmit buffer empty; Interrupt Flag: UCTXIFG0
#ifdef DEBUG_I2C
      if (debug_str_ptr) *debug_str_ptr++ = 'T';
#endif
      I2C_DEV->IF_CLR = I2C_IF_TXBL;
      switch  (i2c_state) {
        case  rcv_reg_hi:
#ifdef DEBUG_I2C
          if (debug_str_ptr) *debug_str_ptr++ = 'h';
#endif
          i2c_state = rcv_reg_lo;
          I2C_DEV->TXDATA = i2c_register[1];
          break;
        case  rcv_reg_lo:
#ifdef DEBUG_I2C
          if (debug_str_ptr) *debug_str_ptr++ = 'l';
#endif
          i2c_state =  rcv_restart;
          BUS_RegMaskedClear(&I2C_DEV->IEN, I2C_IEN_TXBL);
          I2C_DEV->TXDATA = i2c_register[0];
          break;
        case send_reg_hi:
#ifdef DEBUG_I2C
          if (debug_str_ptr) *debug_str_ptr++ = 'n';
#endif
          i2c_state = send_reg_lo;
          I2C_DEV->TXDATA = i2c_register[1];
          break;
        case send_reg_lo:
#ifdef DEBUG_I2C
          if (debug_str_ptr) *debug_str_ptr++ = 'n';
#endif
          i2c_state = send_data;
          I2C_DEV->TXDATA = i2c_register[0];
          break;
        case send_data:
#ifdef DEBUG_I2C
          if (debug_str_ptr) *debug_str_ptr++ = 'd';
#endif
          if (data_count) {
              I2C_DEV->TXDATA = *data_ptr++;
              --data_count;
          } else {
              i2c_state = i2c_stop;
              BUS_RegMaskedClear(&I2C_DEV->IEN, I2C_IEN_TXBL);
          }
          break;

        default:
          break;
      }
  }
  if ((I2C_DEV->IEN & I2C_IEN_MSTOP) && (I2C_DEV->IF & I2C_IF_MSTOP)) { // Stop condition received; Interrupt Flag: UCSTPIFG
#ifdef DEBUG_I2C
      if (debug_str_ptr) *debug_str_ptr++ = '\0';
      debug_str_ptr = 0;
#endif
      i2c_state = i2c_idle;
      I2C_DEV->CMD = I2C_CMD_CLEARTX | I2C_CMD_CLEARPC;
      I2C_DEV->IF_CLR = I2C_IF_MSTOP;
      BUS_RegMaskedClear(&I2C_DEV->IEN, I2C_IEN_TXBL);
  }
  if ((I2C_DEV->IEN & I2C_IEN_ACK) && (I2C_DEV->IF & I2C_IF_ACK)) { // Not acknowledgment; Interrupt Flag: UCNACKIFG
#ifdef DEBUG_I2C
          if (debug_str_ptr) *debug_str_ptr++ = 'K';
#endif
      I2C_DEV->IF_CLR = I2C_IF_ACK;
      if (i2c_state == i2c_stop) {
          I2C_DEV->CMD = I2C_CMD_STOP;
      }
      if (i2c_state == rcv_restart) {
          i2c_state = rcv_data;
          i2c_addr |= 0x01;
          I2C_DEV->CMD = I2C_CMD_START;
      }
  }

  if ((I2C_DEV->IEN & I2C_IEN_NACK) && (I2C_DEV->IF & I2C_IF_NACK)) { // Not acknowledgment; Interrupt Flag: UCNACKIFG
#ifdef DEBUG_I2C
      if (debug_str_ptr) *debug_str_ptr++ = 'N';
#endif
      i2c_error_code = I2C_NAK_ADDR;
      i2c_state = i2c_stop;
      I2C_DEV->IF_CLR = I2C_IF_NACK;
      BUS_RegMaskedClear(&I2C_DEV->IEN, I2C_IEN_TXBL);
      I2C_DEV->CMD = I2C_CMD_STOP;
  }
}


t_i2c_status i2c_wr(uint8_t address, unsigned char * data, unsigned int length) {

  if (i2c_state != i2c_idle) return I2C_BUSY;

  data_ptr = data;
  data_count = length;
  //  i2c_register = reg_addr;
  i2c_state = send_data;
  i2c_error_code = I2C_SUCCESS;
  i2c_addr = address & ~0x01;
  I2C_DEV->CMD = I2C_CMD_START;

  while (i2c_state != i2c_idle) continue;
  return i2c_error_code;
}

t_i2c_status i2c_rd(uint8_t address, unsigned char * data, unsigned int length) {

  if (i2c_state != i2c_idle) return I2C_BUSY;
    data_ptr = data;
    data_count = length;
    i2c_state = rcv_data;
    i2c_addr = address |= 0x01;
    i2c_error_code = I2C_SUCCESS;
    I2C_DEV->CMD = I2C_CMD_START;

    while (i2c_state != i2c_idle) continue;
    return i2c_error_code;
}

t_i2c_status i2c_wr_reg(uint8_t address, uint8_t reg_addr, unsigned char * data, unsigned int length) {

  if (i2c_state != i2c_idle) return I2C_BUSY;

  data_ptr = data;
  data_count = length;
  i2c_register[0] = reg_addr;
  i2c_state = send_reg_lo;
  i2c_error_code = I2C_SUCCESS;
  i2c_addr = address & ~0x01;
  I2C_DEV->CMD = I2C_CMD_START;

  while (i2c_state != i2c_idle) continue;
  return i2c_error_code;
}

t_i2c_status i2c_rd_reg(uint8_t address, uint8_t reg_addr, unsigned char * data, unsigned int length) {

  if (i2c_state != i2c_idle) return I2C_BUSY;
  data_ptr = data;
  data_count = length;
  i2c_register[0] = reg_addr;
  i2c_state = rcv_reg_lo;
  i2c_addr = address & ~0x01;
  i2c_error_code = I2C_SUCCESS;
  I2C_DEV->CMD = I2C_CMD_START;

  while (i2c_state != i2c_idle) continue;
  return i2c_error_code;
}

t_i2c_status i2c_wr_reg16(uint8_t address, uint16_t reg_addr, unsigned char * data, unsigned int length) {

  if (i2c_state != i2c_idle) return I2C_BUSY;

  data_ptr = data;
  data_count = length;
  i2c_register[0] = (reg_addr & 0x00FF);
  i2c_register[1] = (reg_addr & 0xff00) >> 8;
  i2c_state = send_reg_lo;
  i2c_error_code = I2C_SUCCESS;
  i2c_addr = address & ~0x01;
  I2C_DEV->CMD = I2C_CMD_START;

  while (i2c_state != i2c_idle) continue;
  return i2c_error_code;
}

t_i2c_status i2c_rd_reg16(uint8_t address, uint16_t reg_addr, unsigned char * data, unsigned int length) {

  if (i2c_state != i2c_idle) return I2C_BUSY;
#ifdef DEBUG_I2C
  debug_str_ptr = debug_str;
#endif

  data_ptr = data;
  data_count = length;
  i2c_register[0] = (reg_addr & 0x00FF);
  i2c_register[1] = (reg_addr & 0xFF00) >> 8;
  i2c_state = rcv_reg_hi;
  i2c_error_code = I2C_SUCCESS;
  i2c_addr = address & ~0x01;
  I2C_DEV->CMD = I2C_CMD_START;

  while (i2c_state != i2c_idle) continue;
  return i2c_error_code;
}

Причем, если последовательно вызывать i2c_wr_reg(0xA8, 0x00, NULL, 0) и i2c_rd(0xA9, ptr, 256) - всё считывается как надо.

Re: i2c на efr32bg24

Сб ноя 19, 2022 12:10:27

Логического анализатора у меня нет, но я догадываюсь

гадать можно сколько угодно, нужен лог. анализатор. если есть bluepill можно в него залить прошивку buck50, там есть функция лог анализатора. лог можно будет скинуть в pulseview и там расшифровать.
Ответить