i2c slave block transfer driver library

Dependents:   i2c_slave_block_example i2c_lora_slave

I2C block read/write slave

operates with I2C master using the i2c_smbus_read_i2c_block_data() and i2c_smbus_write_i2c_block_data() calls.

Tested with raspberry pi as master.
STM32Lx nucleo as slave.

signal pinmbed slaveRPi master
GND6
SCLD155
SDAD143
IRQany avail pinany avail pin

IRQ pin is implemented in application, not in this library. (see example)

Up to 32byte length per transfer.
Raspberry pi I2C doesnt support SMBUS block read, so I2C block transfers are supported here.
SMBUS block transfers are variable length. Instead, here in this driver, I2C block length is mapped to command byte by const array cmd_to_length[] (see example)

master write to slave

For i2c_smbus_write_i2c_block_data() from master, this driver lets the main loop on slave take the write request. The main loop on slave calls service_i2c(), which then calls service_i2c_write() upon each write from master. This occurs thru a circular buffer, permitting the slave to take its time doing the request, while the master can issue several back-to-back requests without delays.

clock stretching

For i2c_smbus_read_i2c_block_data(): Since raspberry pi doesnt support clock stretching out the the box, providing transmit data must be done in the interrupt service routine. fill_tx_buf() populates the transmit buffer to be sent to master, which must be done immediately because this occurs inside interrupt handler. If I2C master were to support clock stretching, then transmit buffer work could be done in main loop of slave.
/media/uploads/dudmuck/i2c_rpi_nucleo_sm_.png
An STM32 running at 32MHz is unlikely to operate with 400KHz I2C. Use 100KHz speed with 32MHz CPU, or use faster speed CPU for 400KHz I2C.

Committer:
Wayne Roberts
Date:
Fri Jan 18 14:25:26 2019 -0800
Revision:
0:20421a857bd5
Child:
1:914409dc83b1
initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Wayne Roberts 0:20421a857bd5 1 #include "smbus.h"
Wayne Roberts 0:20421a857bd5 2 #include "device.h"
Wayne Roberts 0:20421a857bd5 3 #include "smbus_defs.h"
Wayne Roberts 0:20421a857bd5 4 #include "cmds.h"
Wayne Roberts 0:20421a857bd5 5
Wayne Roberts 0:20421a857bd5 6
Wayne Roberts 0:20421a857bd5 7 SMBUS_HandleTypeDef SmbusHandle;
Wayne Roberts 0:20421a857bd5 8
Wayne Roberts 0:20421a857bd5 9 static volatile state_e state;
Wayne Roberts 0:20421a857bd5 10
Wayne Roberts 0:20421a857bd5 11 i2c_slave_t i2c;
Wayne Roberts 0:20421a857bd5 12
Wayne Roberts 0:20421a857bd5 13 static inline void put_cbuf(uint8_t o)
Wayne Roberts 0:20421a857bd5 14 {
Wayne Roberts 0:20421a857bd5 15 i2c.cbuf[i2c.cbuf_in] = o;
Wayne Roberts 0:20421a857bd5 16 if (++i2c.cbuf_in == _RX_BUF_SIZE)
Wayne Roberts 0:20421a857bd5 17 i2c.cbuf_in = 0;
Wayne Roberts 0:20421a857bd5 18 }
Wayne Roberts 0:20421a857bd5 19
Wayne Roberts 0:20421a857bd5 20 static volatile uint8_t i2c_buf_idx;
Wayne Roberts 0:20421a857bd5 21
Wayne Roberts 0:20421a857bd5 22 #ifdef TARGET_STM32L4
Wayne Roberts 0:20421a857bd5 23 void SMBUSx_ER_IRQHandler()
Wayne Roberts 0:20421a857bd5 24 {
Wayne Roberts 0:20421a857bd5 25 asm("nop"); // TODO
Wayne Roberts 0:20421a857bd5 26 }
Wayne Roberts 0:20421a857bd5 27 #endif /* TARGET_STM32L4 */
Wayne Roberts 0:20421a857bd5 28
Wayne Roberts 0:20421a857bd5 29 #ifdef TARGET_STM32L4
Wayne Roberts 0:20421a857bd5 30 void SMBUSx_EV_IRQHandler(void)
Wayne Roberts 0:20421a857bd5 31 #else
Wayne Roberts 0:20421a857bd5 32 void SMBUSx_IRQHandler(void)
Wayne Roberts 0:20421a857bd5 33 #endif
Wayne Roberts 0:20421a857bd5 34 {
Wayne Roberts 0:20421a857bd5 35 uint32_t tmpisrvalue = SMBUS_GET_ISR_REG(&SmbusHandle);
Wayne Roberts 0:20421a857bd5 36 uint32_t icr = 0;
Wayne Roberts 0:20421a857bd5 37 static uint8_t xfer_length;
Wayne Roberts 0:20421a857bd5 38 static bool i2cTransferDirection; // true = read
Wayne Roberts 0:20421a857bd5 39 static uint8_t cmd;
Wayne Roberts 0:20421a857bd5 40 static uint8_t rx_buf_[32];
Wayne Roberts 0:20421a857bd5 41
Wayne Roberts 0:20421a857bd5 42 if (tmpisrvalue & SMBUS_FLAG_RXNE) {
Wayne Roberts 0:20421a857bd5 43 uint8_t rxdr = SmbusHandle.Instance->RXDR;
Wayne Roberts 0:20421a857bd5 44 if (state == STATE_CMD) {
Wayne Roberts 0:20421a857bd5 45 cmd = rxdr;
Wayne Roberts 0:20421a857bd5 46 SmbusHandle.Instance->CR2 &= ~I2C_CR2_NACK; // ack last byte read
Wayne Roberts 0:20421a857bd5 47 xfer_length = cmd_to_length[cmd];
Wayne Roberts 0:20421a857bd5 48 i2c_buf_idx = 0;
Wayne Roberts 0:20421a857bd5 49 state = STATE_XFERING;
Wayne Roberts 0:20421a857bd5 50 } else if (state == STATE_XFERING && i2cTransferDirection == 0) { // if xfering host write
Wayne Roberts 0:20421a857bd5 51 if (i2c_buf_idx < sizeof(rx_buf_))
Wayne Roberts 0:20421a857bd5 52 rx_buf_[i2c_buf_idx++] = rxdr;
Wayne Roberts 0:20421a857bd5 53 }
Wayne Roberts 0:20421a857bd5 54
Wayne Roberts 0:20421a857bd5 55 icr |= SMBUS_FLAG_RXNE;
Wayne Roberts 0:20421a857bd5 56 }
Wayne Roberts 0:20421a857bd5 57
Wayne Roberts 0:20421a857bd5 58
Wayne Roberts 0:20421a857bd5 59 if (tmpisrvalue & SMBUS_FLAG_ADDR) {
Wayne Roberts 0:20421a857bd5 60 i2cTransferDirection = SMBUS_GET_DIR(&SmbusHandle);
Wayne Roberts 0:20421a857bd5 61 if (i2cTransferDirection) {
Wayne Roberts 0:20421a857bd5 62 // 1: Read transfer, slave enters transmitter mode.. cmd has already been provided
Wayne Roberts 0:20421a857bd5 63 fill_tx_buf(cmd);
Wayne Roberts 0:20421a857bd5 64
Wayne Roberts 0:20421a857bd5 65 __HAL_SMBUS_ENABLE_IT(&SmbusHandle, SMBUS_IT_ERRI | SMBUS_IT_TCI | SMBUS_IT_STOPI | SMBUS_IT_NACKI | SMBUS_IT_TXI);
Wayne Roberts 0:20421a857bd5 66 } else {
Wayne Roberts 0:20421a857bd5 67 // 0: Write transfer, slave enters receiver mode.
Wayne Roberts 0:20421a857bd5 68 __HAL_SMBUS_ENABLE_IT(&SmbusHandle, SMBUS_IT_ERRI | SMBUS_IT_TCI | SMBUS_IT_STOPI | SMBUS_IT_NACKI | SMBUS_IT_RXI);
Wayne Roberts 0:20421a857bd5 69 if (state == STATE_NONE) {
Wayne Roberts 0:20421a857bd5 70 state = STATE_CMD;
Wayne Roberts 0:20421a857bd5 71 }
Wayne Roberts 0:20421a857bd5 72 }
Wayne Roberts 0:20421a857bd5 73
Wayne Roberts 0:20421a857bd5 74 icr |= SMBUS_FLAG_ADDR;
Wayne Roberts 0:20421a857bd5 75 }
Wayne Roberts 0:20421a857bd5 76
Wayne Roberts 0:20421a857bd5 77 if (tmpisrvalue & SMBUS_FLAG_AF) {
Wayne Roberts 0:20421a857bd5 78 /* last byte the host wants was sent */
Wayne Roberts 0:20421a857bd5 79 icr |= SMBUS_FLAG_AF;
Wayne Roberts 0:20421a857bd5 80 }
Wayne Roberts 0:20421a857bd5 81
Wayne Roberts 0:20421a857bd5 82 if (tmpisrvalue & SMBUS_FLAG_STOPF) {
Wayne Roberts 0:20421a857bd5 83 icr |= SMBUS_FLAG_STOPF;
Wayne Roberts 0:20421a857bd5 84 if (i2cTransferDirection == 0) { // host done writing to buf
Wayne Roberts 0:20421a857bd5 85 unsigned i;
Wayne Roberts 0:20421a857bd5 86 /* send to mainloop */
Wayne Roberts 0:20421a857bd5 87 put_cbuf(cmd);
Wayne Roberts 0:20421a857bd5 88 put_cbuf(i2c_buf_idx);
Wayne Roberts 0:20421a857bd5 89 for (i = 0; i < i2c_buf_idx; i++)
Wayne Roberts 0:20421a857bd5 90 put_cbuf(rx_buf_[i]);
Wayne Roberts 0:20421a857bd5 91 }
Wayne Roberts 0:20421a857bd5 92
Wayne Roberts 0:20421a857bd5 93 if (!(tmpisrvalue & SMBUS_FLAG_TXE)) {
Wayne Roberts 0:20421a857bd5 94 /* slave wanted to send more than host wanted to receive */
Wayne Roberts 0:20421a857bd5 95 SmbusHandle.Instance->ISR = SMBUS_FLAG_TXE;
Wayne Roberts 0:20421a857bd5 96 }
Wayne Roberts 0:20421a857bd5 97
Wayne Roberts 0:20421a857bd5 98 state = STATE_NONE;
Wayne Roberts 0:20421a857bd5 99 }
Wayne Roberts 0:20421a857bd5 100
Wayne Roberts 0:20421a857bd5 101 if (tmpisrvalue & SMBUS_FLAG_TXIS ) {
Wayne Roberts 0:20421a857bd5 102 if (state == STATE_XFERING && i2cTransferDirection) { // xfering in host-read direction
Wayne Roberts 0:20421a857bd5 103 SmbusHandle.Instance->TXDR = i2c.tx_buf[i2c_buf_idx++];
Wayne Roberts 0:20421a857bd5 104 if (i2c_buf_idx == xfer_length) {
Wayne Roberts 0:20421a857bd5 105 state = STATE_SEND_DONE;
Wayne Roberts 0:20421a857bd5 106 __HAL_SMBUS_DISABLE_IT(&SmbusHandle, SMBUS_IT_TXI);
Wayne Roberts 0:20421a857bd5 107 }
Wayne Roberts 0:20421a857bd5 108 }
Wayne Roberts 0:20421a857bd5 109 } else if (icr == 0) {
Wayne Roberts 0:20421a857bd5 110 /* unhandled interrupt flag: halt this irq */
Wayne Roberts 0:20421a857bd5 111 #ifdef TARGET_STM32L4
Wayne Roberts 0:20421a857bd5 112 HAL_NVIC_DisableIRQ(SMBUSx_EV_IRQn);
Wayne Roberts 0:20421a857bd5 113 #else
Wayne Roberts 0:20421a857bd5 114 HAL_NVIC_DisableIRQ(SMBUSx_IRQn);
Wayne Roberts 0:20421a857bd5 115 #endif
Wayne Roberts 0:20421a857bd5 116 }
Wayne Roberts 0:20421a857bd5 117
Wayne Roberts 0:20421a857bd5 118 __HAL_SMBUS_CLEAR_FLAG(&SmbusHandle, icr);
Wayne Roberts 0:20421a857bd5 119 }
Wayne Roberts 0:20421a857bd5 120
Wayne Roberts 0:20421a857bd5 121 int smbus_init(uint8_t slaveAddress)
Wayne Roberts 0:20421a857bd5 122 {
Wayne Roberts 0:20421a857bd5 123 uint32_t tmpisr;
Wayne Roberts 0:20421a857bd5 124 //HAL_StatusTypeDef status;
Wayne Roberts 0:20421a857bd5 125
Wayne Roberts 0:20421a857bd5 126 SmbusHandle.Instance = I2Cx;
Wayne Roberts 0:20421a857bd5 127
Wayne Roberts 0:20421a857bd5 128 SmbusHandle.Init.Timing = I2C_TIMING;
Wayne Roberts 0:20421a857bd5 129 SmbusHandle.Init.AnalogFilter = SMBUS_ANALOGFILTER_ENABLE;
Wayne Roberts 0:20421a857bd5 130 SmbusHandle.Init.OwnAddress1 = slaveAddress;
Wayne Roberts 0:20421a857bd5 131 SmbusHandle.Init.AddressingMode = SMBUS_ADDRESSINGMODE_7BIT;
Wayne Roberts 0:20421a857bd5 132 SmbusHandle.Init.DualAddressMode = SMBUS_DUALADDRESS_DISABLE;
Wayne Roberts 0:20421a857bd5 133 SmbusHandle.Init.OwnAddress2 = 0x00;
Wayne Roberts 0:20421a857bd5 134 SmbusHandle.Init.GeneralCallMode = SMBUS_GENERALCALL_DISABLE;
Wayne Roberts 0:20421a857bd5 135 SmbusHandle.Init.NoStretchMode = SMBUS_NOSTRETCH_DISABLE;
Wayne Roberts 0:20421a857bd5 136 SmbusHandle.Init.PacketErrorCheckMode = SMBUS_PEC_DISABLE;
Wayne Roberts 0:20421a857bd5 137 SmbusHandle.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_SLAVE;
Wayne Roberts 0:20421a857bd5 138 SmbusHandle.Init.SMBusTimeout = 0;
Wayne Roberts 0:20421a857bd5 139
Wayne Roberts 0:20421a857bd5 140 if(HAL_SMBUS_Init(&SmbusHandle) != HAL_OK)
Wayne Roberts 0:20421a857bd5 141 {
Wayne Roberts 0:20421a857bd5 142 return -1;
Wayne Roberts 0:20421a857bd5 143 }
Wayne Roberts 0:20421a857bd5 144
Wayne Roberts 0:20421a857bd5 145 state = STATE_NONE;
Wayne Roberts 0:20421a857bd5 146
Wayne Roberts 0:20421a857bd5 147 tmpisr = SMBUS_IT_ADDRI | SMBUS_IT_STOPI | SMBUS_IT_NACKI | SMBUS_IT_ERRI;
Wayne Roberts 0:20421a857bd5 148 __HAL_SMBUS_ENABLE_IT(&SmbusHandle, tmpisr);
Wayne Roberts 0:20421a857bd5 149
Wayne Roberts 0:20421a857bd5 150 return 0;
Wayne Roberts 0:20421a857bd5 151 }
Wayne Roberts 0:20421a857bd5 152
Wayne Roberts 0:20421a857bd5 153 static inline uint8_t get_cbuf()
Wayne Roberts 0:20421a857bd5 154 {
Wayne Roberts 0:20421a857bd5 155 uint8_t ret = i2c.cbuf[i2c.cbuf_out];
Wayne Roberts 0:20421a857bd5 156 if (++i2c.cbuf_out == _RX_BUF_SIZE)
Wayne Roberts 0:20421a857bd5 157 i2c.cbuf_out = 0;
Wayne Roberts 0:20421a857bd5 158
Wayne Roberts 0:20421a857bd5 159 return ret;
Wayne Roberts 0:20421a857bd5 160 }
Wayne Roberts 0:20421a857bd5 161
Wayne Roberts 0:20421a857bd5 162 /* service_i2c(): call from main loop */
Wayne Roberts 0:20421a857bd5 163 void service_i2c()
Wayne Roberts 0:20421a857bd5 164 {
Wayne Roberts 0:20421a857bd5 165 if (i2c.cbuf_in != i2c.cbuf_out) {
Wayne Roberts 0:20421a857bd5 166 uint8_t n, cmd, len;
Wayne Roberts 0:20421a857bd5 167 cmd = get_cbuf();
Wayne Roberts 0:20421a857bd5 168 len = get_cbuf();
Wayne Roberts 0:20421a857bd5 169 uint8_t buf[32];
Wayne Roberts 0:20421a857bd5 170 /* host is writing: parse here */
Wayne Roberts 0:20421a857bd5 171 for (n = 0; n < len; n++)
Wayne Roberts 0:20421a857bd5 172 buf[n] = get_cbuf();
Wayne Roberts 0:20421a857bd5 173
Wayne Roberts 0:20421a857bd5 174 /*if (len != cmd_to_length[cmd])
Wayne Roberts 0:20421a857bd5 175 pc.printf("[%02x != %02x] ", len, cmd_to_length[cmd]);*/
Wayne Roberts 0:20421a857bd5 176
Wayne Roberts 0:20421a857bd5 177 service_i2c_write(cmd, len, buf);
Wayne Roberts 0:20421a857bd5 178 }
Wayne Roberts 0:20421a857bd5 179 }
Wayne Roberts 0:20421a857bd5 180