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:
Mon Jan 21 16:58:05 2019 -0800
Revision:
1:914409dc83b1
Parent:
0:20421a857bd5
Child:
2:ff709de859ed
Child:
3:c012313ebc13
cmd_allowed() lets application decide if command is permitted

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 1:914409dc83b1 46 if (cmd_allowed(cmd)) {
Wayne Roberts 1:914409dc83b1 47 xfer_length = cmd_to_length[cmd];
Wayne Roberts 1:914409dc83b1 48 i2c_buf_idx = 0;
Wayne Roberts 1:914409dc83b1 49 state = STATE_XFERING;
Wayne Roberts 1:914409dc83b1 50 } else {
Wayne Roberts 1:914409dc83b1 51 state = STATE_NONE;
Wayne Roberts 1:914409dc83b1 52 __HAL_SMBUS_GENERATE_NACK(&SmbusHandle);
Wayne Roberts 1:914409dc83b1 53 }
Wayne Roberts 0:20421a857bd5 54 } else if (state == STATE_XFERING && i2cTransferDirection == 0) { // if xfering host write
Wayne Roberts 0:20421a857bd5 55 if (i2c_buf_idx < sizeof(rx_buf_))
Wayne Roberts 0:20421a857bd5 56 rx_buf_[i2c_buf_idx++] = rxdr;
Wayne Roberts 0:20421a857bd5 57 }
Wayne Roberts 0:20421a857bd5 58
Wayne Roberts 0:20421a857bd5 59 icr |= SMBUS_FLAG_RXNE;
Wayne Roberts 0:20421a857bd5 60 }
Wayne Roberts 0:20421a857bd5 61
Wayne Roberts 0:20421a857bd5 62
Wayne Roberts 0:20421a857bd5 63 if (tmpisrvalue & SMBUS_FLAG_ADDR) {
Wayne Roberts 0:20421a857bd5 64 i2cTransferDirection = SMBUS_GET_DIR(&SmbusHandle);
Wayne Roberts 0:20421a857bd5 65 if (i2cTransferDirection) {
Wayne Roberts 0:20421a857bd5 66 // 1: Read transfer, slave enters transmitter mode.. cmd has already been provided
Wayne Roberts 0:20421a857bd5 67 fill_tx_buf(cmd);
Wayne Roberts 0:20421a857bd5 68
Wayne Roberts 0:20421a857bd5 69 __HAL_SMBUS_ENABLE_IT(&SmbusHandle, SMBUS_IT_ERRI | SMBUS_IT_TCI | SMBUS_IT_STOPI | SMBUS_IT_NACKI | SMBUS_IT_TXI);
Wayne Roberts 0:20421a857bd5 70 } else {
Wayne Roberts 0:20421a857bd5 71 // 0: Write transfer, slave enters receiver mode.
Wayne Roberts 0:20421a857bd5 72 __HAL_SMBUS_ENABLE_IT(&SmbusHandle, SMBUS_IT_ERRI | SMBUS_IT_TCI | SMBUS_IT_STOPI | SMBUS_IT_NACKI | SMBUS_IT_RXI);
Wayne Roberts 0:20421a857bd5 73 if (state == STATE_NONE) {
Wayne Roberts 0:20421a857bd5 74 state = STATE_CMD;
Wayne Roberts 0:20421a857bd5 75 }
Wayne Roberts 0:20421a857bd5 76 }
Wayne Roberts 0:20421a857bd5 77
Wayne Roberts 0:20421a857bd5 78 icr |= SMBUS_FLAG_ADDR;
Wayne Roberts 0:20421a857bd5 79 }
Wayne Roberts 0:20421a857bd5 80
Wayne Roberts 0:20421a857bd5 81 if (tmpisrvalue & SMBUS_FLAG_AF) {
Wayne Roberts 0:20421a857bd5 82 /* last byte the host wants was sent */
Wayne Roberts 0:20421a857bd5 83 icr |= SMBUS_FLAG_AF;
Wayne Roberts 0:20421a857bd5 84 }
Wayne Roberts 0:20421a857bd5 85
Wayne Roberts 0:20421a857bd5 86 if (tmpisrvalue & SMBUS_FLAG_STOPF) {
Wayne Roberts 0:20421a857bd5 87 icr |= SMBUS_FLAG_STOPF;
Wayne Roberts 0:20421a857bd5 88 if (i2cTransferDirection == 0) { // host done writing to buf
Wayne Roberts 0:20421a857bd5 89 unsigned i;
Wayne Roberts 0:20421a857bd5 90 /* send to mainloop */
Wayne Roberts 0:20421a857bd5 91 put_cbuf(cmd);
Wayne Roberts 0:20421a857bd5 92 put_cbuf(i2c_buf_idx);
Wayne Roberts 0:20421a857bd5 93 for (i = 0; i < i2c_buf_idx; i++)
Wayne Roberts 0:20421a857bd5 94 put_cbuf(rx_buf_[i]);
Wayne Roberts 0:20421a857bd5 95 }
Wayne Roberts 0:20421a857bd5 96
Wayne Roberts 0:20421a857bd5 97 if (!(tmpisrvalue & SMBUS_FLAG_TXE)) {
Wayne Roberts 0:20421a857bd5 98 /* slave wanted to send more than host wanted to receive */
Wayne Roberts 0:20421a857bd5 99 SmbusHandle.Instance->ISR = SMBUS_FLAG_TXE;
Wayne Roberts 0:20421a857bd5 100 }
Wayne Roberts 0:20421a857bd5 101
Wayne Roberts 0:20421a857bd5 102 state = STATE_NONE;
Wayne Roberts 0:20421a857bd5 103 }
Wayne Roberts 0:20421a857bd5 104
Wayne Roberts 0:20421a857bd5 105 if (tmpisrvalue & SMBUS_FLAG_TXIS ) {
Wayne Roberts 0:20421a857bd5 106 if (state == STATE_XFERING && i2cTransferDirection) { // xfering in host-read direction
Wayne Roberts 0:20421a857bd5 107 SmbusHandle.Instance->TXDR = i2c.tx_buf[i2c_buf_idx++];
Wayne Roberts 0:20421a857bd5 108 if (i2c_buf_idx == xfer_length) {
Wayne Roberts 0:20421a857bd5 109 state = STATE_SEND_DONE;
Wayne Roberts 0:20421a857bd5 110 __HAL_SMBUS_DISABLE_IT(&SmbusHandle, SMBUS_IT_TXI);
Wayne Roberts 0:20421a857bd5 111 }
Wayne Roberts 0:20421a857bd5 112 }
Wayne Roberts 0:20421a857bd5 113 } else if (icr == 0) {
Wayne Roberts 0:20421a857bd5 114 /* unhandled interrupt flag: halt this irq */
Wayne Roberts 0:20421a857bd5 115 #ifdef TARGET_STM32L4
Wayne Roberts 0:20421a857bd5 116 HAL_NVIC_DisableIRQ(SMBUSx_EV_IRQn);
Wayne Roberts 0:20421a857bd5 117 #else
Wayne Roberts 0:20421a857bd5 118 HAL_NVIC_DisableIRQ(SMBUSx_IRQn);
Wayne Roberts 0:20421a857bd5 119 #endif
Wayne Roberts 0:20421a857bd5 120 }
Wayne Roberts 0:20421a857bd5 121
Wayne Roberts 0:20421a857bd5 122 __HAL_SMBUS_CLEAR_FLAG(&SmbusHandle, icr);
Wayne Roberts 0:20421a857bd5 123 }
Wayne Roberts 0:20421a857bd5 124
Wayne Roberts 0:20421a857bd5 125 int smbus_init(uint8_t slaveAddress)
Wayne Roberts 0:20421a857bd5 126 {
Wayne Roberts 0:20421a857bd5 127 uint32_t tmpisr;
Wayne Roberts 0:20421a857bd5 128 //HAL_StatusTypeDef status;
Wayne Roberts 0:20421a857bd5 129
Wayne Roberts 0:20421a857bd5 130 SmbusHandle.Instance = I2Cx;
Wayne Roberts 0:20421a857bd5 131
Wayne Roberts 0:20421a857bd5 132 SmbusHandle.Init.Timing = I2C_TIMING;
Wayne Roberts 0:20421a857bd5 133 SmbusHandle.Init.AnalogFilter = SMBUS_ANALOGFILTER_ENABLE;
Wayne Roberts 0:20421a857bd5 134 SmbusHandle.Init.OwnAddress1 = slaveAddress;
Wayne Roberts 0:20421a857bd5 135 SmbusHandle.Init.AddressingMode = SMBUS_ADDRESSINGMODE_7BIT;
Wayne Roberts 0:20421a857bd5 136 SmbusHandle.Init.DualAddressMode = SMBUS_DUALADDRESS_DISABLE;
Wayne Roberts 0:20421a857bd5 137 SmbusHandle.Init.OwnAddress2 = 0x00;
Wayne Roberts 0:20421a857bd5 138 SmbusHandle.Init.GeneralCallMode = SMBUS_GENERALCALL_DISABLE;
Wayne Roberts 0:20421a857bd5 139 SmbusHandle.Init.NoStretchMode = SMBUS_NOSTRETCH_DISABLE;
Wayne Roberts 0:20421a857bd5 140 SmbusHandle.Init.PacketErrorCheckMode = SMBUS_PEC_DISABLE;
Wayne Roberts 0:20421a857bd5 141 SmbusHandle.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_SLAVE;
Wayne Roberts 0:20421a857bd5 142 SmbusHandle.Init.SMBusTimeout = 0;
Wayne Roberts 0:20421a857bd5 143
Wayne Roberts 0:20421a857bd5 144 if(HAL_SMBUS_Init(&SmbusHandle) != HAL_OK)
Wayne Roberts 0:20421a857bd5 145 {
Wayne Roberts 0:20421a857bd5 146 return -1;
Wayne Roberts 0:20421a857bd5 147 }
Wayne Roberts 0:20421a857bd5 148
Wayne Roberts 0:20421a857bd5 149 state = STATE_NONE;
Wayne Roberts 0:20421a857bd5 150
Wayne Roberts 0:20421a857bd5 151 tmpisr = SMBUS_IT_ADDRI | SMBUS_IT_STOPI | SMBUS_IT_NACKI | SMBUS_IT_ERRI;
Wayne Roberts 0:20421a857bd5 152 __HAL_SMBUS_ENABLE_IT(&SmbusHandle, tmpisr);
Wayne Roberts 0:20421a857bd5 153
Wayne Roberts 0:20421a857bd5 154 return 0;
Wayne Roberts 0:20421a857bd5 155 }
Wayne Roberts 0:20421a857bd5 156
Wayne Roberts 0:20421a857bd5 157 static inline uint8_t get_cbuf()
Wayne Roberts 0:20421a857bd5 158 {
Wayne Roberts 0:20421a857bd5 159 uint8_t ret = i2c.cbuf[i2c.cbuf_out];
Wayne Roberts 0:20421a857bd5 160 if (++i2c.cbuf_out == _RX_BUF_SIZE)
Wayne Roberts 0:20421a857bd5 161 i2c.cbuf_out = 0;
Wayne Roberts 0:20421a857bd5 162
Wayne Roberts 0:20421a857bd5 163 return ret;
Wayne Roberts 0:20421a857bd5 164 }
Wayne Roberts 0:20421a857bd5 165
Wayne Roberts 0:20421a857bd5 166 /* service_i2c(): call from main loop */
Wayne Roberts 0:20421a857bd5 167 void service_i2c()
Wayne Roberts 0:20421a857bd5 168 {
Wayne Roberts 0:20421a857bd5 169 if (i2c.cbuf_in != i2c.cbuf_out) {
Wayne Roberts 0:20421a857bd5 170 uint8_t n, cmd, len;
Wayne Roberts 0:20421a857bd5 171 cmd = get_cbuf();
Wayne Roberts 0:20421a857bd5 172 len = get_cbuf();
Wayne Roberts 0:20421a857bd5 173 uint8_t buf[32];
Wayne Roberts 0:20421a857bd5 174 /* host is writing: parse here */
Wayne Roberts 0:20421a857bd5 175 for (n = 0; n < len; n++)
Wayne Roberts 0:20421a857bd5 176 buf[n] = get_cbuf();
Wayne Roberts 0:20421a857bd5 177
Wayne Roberts 0:20421a857bd5 178 /*if (len != cmd_to_length[cmd])
Wayne Roberts 0:20421a857bd5 179 pc.printf("[%02x != %02x] ", len, cmd_to_length[cmd]);*/
Wayne Roberts 0:20421a857bd5 180
Wayne Roberts 0:20421a857bd5 181 service_i2c_write(cmd, len, buf);
Wayne Roberts 0:20421a857bd5 182 }
Wayne Roberts 0:20421a857bd5 183 }
Wayne Roberts 0:20421a857bd5 184