| Форум РадиоКот https://radiokot.ru/forum/ |
|
| i2c на efr32bg24 https://radiokot.ru/forum/viewtopic.php?f=59&t=185567 |
Страница 1 из 1 |
| Автор: | uldemir [ Сб ноя 19, 2022 11:36:08 ] |
| Заголовок сообщения: | i2c на efr32bg24 |
У меня проблема с написанием драйвера мастера 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) - всё считывается как надо. |
|
| Автор: | JackSmith [ Сб ноя 19, 2022 12:10:27 ] |
| Заголовок сообщения: | Re: i2c на efr32bg24 |
Цитата: Логического анализатора у меня нет, но я догадываюсь гадать можно сколько угодно, нужен лог. анализатор. если есть bluepill можно в него залить прошивку buck50, там есть функция лог анализатора. лог можно будет скинуть в pulseview и там расшифровать. |
|
| Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|


