diff --git a/flight/AHRS/inc/pios_board.h b/flight/AHRS/inc/pios_board.h index 3097cba76..1cda2580b 100644 --- a/flight/AHRS/inc/pios_board.h +++ b/flight/AHRS/inc/pios_board.h @@ -99,21 +99,9 @@ TIM8 | | | | //------------------------ // PIOS_I2C +// See also pios_board.c //------------------------ -#define PIOS_I2C_PORT I2C1 -#define PIOS_I2C_CLK RCC_APB1Periph_I2C1 -#define PIOS_I2C_GPIO_PORT GPIOB -#define PIOS_I2C_SCL_PIN GPIO_Pin_6 -#define PIOS_I2C_SDA_PIN GPIO_Pin_7 -#define PIOS_I2C_DUTY_CYCLE I2C_DutyCycle_2 -#define PIOS_I2C_BUS_FREQ 400000 // bit/s -#define PIOS_I2C_TIMEOUT_VALUE 50 // ms -#define PIOS_I2C_IRQ_EV_HANDLER void I2C1_EV_IRQHandler(void) -#define PIOS_I2C_IRQ_ER_HANDLER void I2C1_ER_IRQHandler(void) -#define PIOS_I2C_IRQ_EV_CHANNEL I2C1_EV_IRQn -#define PIOS_I2C_IRQ_ER_CHANNEL I2C1_EV_IRQn -#define PIOS_I2C_IRQ_EV_PRIORITY 2 -#define PIOS_I2C_IRQ_ER_PRIORITY 2 +#define PIOS_I2C_MAIN_ADAPTER 0 //------------------------- // SPI diff --git a/flight/AHRS/pios_board.c b/flight/AHRS/pios_board.c index fed25bff5..abd774326 100644 --- a/flight/AHRS/pios_board.c +++ b/flight/AHRS/pios_board.c @@ -233,3 +233,90 @@ const uint8_t pios_com_num_devices = NELEMENTS(pios_com_devs); #endif /* PIOS_INCLUDE_COM */ +#if defined(PIOS_INCLUDE_I2C) +#include + +/* + * I2C Adapters + */ + +void PIOS_I2C_main_adapter_ev_irq_handler(void); +void PIOS_I2C_main_adapter_er_irq_handler(void); +void I2C1_EV_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_ev_irq_handler"))); +void I2C1_ER_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_er_irq_handler"))); + +const struct pios_i2c_adapter_cfg pios_i2c_main_adapter_cfg = { + .regs = I2C1, + .init = { + .I2C_Mode = I2C_Mode_I2C, + .I2C_OwnAddress1 = 0, + .I2C_Ack = I2C_Ack_Enable, + .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, + .I2C_DutyCycle = I2C_DutyCycle_2, + .I2C_ClockSpeed = 200000, /* bits/s */ + }, + .transfer_timeout_ms = 50, + .scl = { + .gpio = GPIOB, + .init = { + .GPIO_Pin = GPIO_Pin_6, + .GPIO_Speed = GPIO_Speed_10MHz, + .GPIO_Mode = GPIO_Mode_AF_OD, + }, + }, + .sda = { + .gpio = GPIOB, + .init = { + .GPIO_Pin = GPIO_Pin_7, + .GPIO_Speed = GPIO_Speed_10MHz, + .GPIO_Mode = GPIO_Mode_AF_OD, + }, + }, + .event = { + .handler = PIOS_I2C_main_adapter_ev_irq_handler, + .flags = 0, /* FIXME: check this */ + .init = { + .NVIC_IRQChannel = I2C1_EV_IRQn, + //.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelPreemptionPriority = 2, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, + .error = { + .handler = PIOS_I2C_main_adapter_er_irq_handler, + .flags = 0, /* FIXME: check this */ + .init = { + .NVIC_IRQChannel = I2C1_ER_IRQn, + //.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelPreemptionPriority = 2, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, +}; + +/* + * Board specific number of devices. + */ +struct pios_i2c_adapter pios_i2c_adapters[] = { + { + .cfg = &pios_i2c_main_adapter_cfg, + }, +}; + +uint8_t pios_i2c_num_adapters = NELEMENTS(pios_i2c_adapters); + +void PIOS_I2C_main_adapter_ev_irq_handler(void) +{ + /* Call into the generic code to handle the IRQ for this specific device */ + PIOS_I2C_EV_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER); +} + +void PIOS_I2C_main_adapter_er_irq_handler(void) +{ + /* Call into the generic code to handle the IRQ for this specific device */ + PIOS_I2C_ER_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER); +} + +#endif /* PIOS_INCLUDE_I2C */ diff --git a/flight/Doc/Architecture/stm32_i2c_fsm.dot b/flight/Doc/Architecture/stm32_i2c_fsm.dot new file mode 100644 index 000000000..267d75df4 --- /dev/null +++ b/flight/Doc/Architecture/stm32_i2c_fsm.dot @@ -0,0 +1,175 @@ +// Regenerate using graphviz/dotty with this command +// dot -Tjpg ./flight/Doc/Architecture/stm32_i2c_fsm.dot > ./flight/Doc/Architecture/stm32_i2c_fsm.jpg + +digraph stm32_i2c_fsm { + label="STM32 I2C FSM" + labelloc=t + labeljust = l + + { + node [ style=filled,color=lightgray ] + + stopped [ shape="doublecircle" ] + } + + node [ style=filled,color=blue ] + + { + rank=same + stopped + stopping + starting + } + + // start addr/w[A] data[A] stop + // SB ADDR BTF + // ADDR DR + // STOP + // + // start addr/w[A] data[A] data[A] stop + // SB ADDR BTF BTF + // ADDR DR DR + // STOP + // + // start addr/w[A] data[A] data[A] data[A] stop + // SB ADDR BTF BTF BTF + // ADDR DR DR DR + // STOP + // + subgraph cluster_w_last_txn { + label="Final Write Txn" + w_last_txn_addr + w_last_txn_middle + w_last_txn_last + } + + // start addr/w[A] data[A] start + // SB ADDR BTF + // ADDR DR + // START + // + // start addr/w[A] data[A] data[A] start + // SB ADDR BTF BTF + // ADDR DR DR + // START + // + // start addr/w[A] data[A] data[A] data[A] stop + // SB ADDR BTF BTF BTF + // ADDR DR DR DR + // START + // + subgraph cluster_w_more_txn { + label="Write Txn with Restart" + w_more_txn_addr + w_more_txn_middle + w_more_txn_last + } + + // start addr/r[A] [data] N stop + // SB ADDR RXNE + // ADDR STOP DR + // NAK + // + // start addr/r[A] [data] A [data] N stop + // SB ADDR RXNE RXNE + // ADDR DR DR + // ACK NAK + // STOP + // + // start addr/r[A] [data] A [data] A [data] N stop + // SB ADDR RXNE RXNE RXNE + // ADDR DR DR DR + // ACK ACK NAK + // STOP + // + subgraph cluster_r_last_txn { + label="Final Read Txn" + r_last_txn_addr + r_last_txn_pre_one + r_last_txn_pre_first + r_last_txn_pre_middle + r_last_txn_pre_last + r_last_txn_post_last + } + + // start addr/r[A] [data] N start + // SB ADDR RXNE + // ADDR START DR + // NAK + // + // start addr/r[A] [data] A [data] N start + // SB ADDR RXNE RXNE + // ADDR DR DR + // ACK NAK + // START + // + // start addr/r[A] [data] A [data] A [data] N start + // SB ADDR RXNE RXNE RXNE + // ADDR DR DR DR + // ACK ACK NAK + // START + subgraph cluster_r_more_txn { + label="Read Txn with Restart" + r_more_txn_addr + r_more_txn_pre_one + r_more_txn_pre_first + r_more_txn_pre_middle + r_more_txn_pre_last + r_more_txn_post_last + } + + stopping -> stopped [ label="auto" ] + stopped -> starting [ label="start" ] + + starting -> w_last_txn_addr [ label="SB\nop=write\nlast txn" ] + starting -> w_more_txn_addr [ label="SB\nop=write\nmore txn" ] + + starting -> r_last_txn_addr [ label="SB\nop=read\nlast txn" ] + starting -> r_more_txn_addr [ label="SB\nop=read\nmore txn" ] + + w_last_txn_addr -> w_last_txn_last [ label="ADDR\nlen==1" ] + w_more_txn_addr -> w_more_txn_last [ label="ADDR\nlen==1" ] + + w_last_txn_addr -> w_last_txn_middle [ label="ADDR\nlen>1" ] + w_more_txn_addr -> w_more_txn_middle [ label="ADDR\nlen>1" ] + + w_last_txn_middle -> w_last_txn_middle [ label="BTF\nlen>1" ] + w_more_txn_middle -> w_more_txn_middle [ label="BTF\nlen>1" ] + + w_last_txn_middle -> w_last_txn_last [ label="BTF\nlen==1" ] + w_more_txn_middle -> w_more_txn_last [ label="BTF\nlen==1" ] + + w_last_txn_last -> stopped [ label="BTF\nlen==0" ] + w_more_txn_last -> starting [ label="BTF\nlen==0" ] + + + r_last_txn_addr -> r_last_txn_pre_one [ label="ADDR\nlen==1" ] + r_more_txn_addr -> r_more_txn_pre_one [ label="ADDR\nlen==1" ] + + r_last_txn_addr -> r_last_txn_pre_last [ label="ADDR\nlen==2" ] + r_more_txn_addr -> r_more_txn_pre_last [ label="ADDR\nlen==2" ] + + r_last_txn_addr -> r_last_txn_pre_first [ label="ADDR\nlen>2" ] + r_more_txn_addr -> r_more_txn_pre_first [ label="ADDR\nlen>2" ] + + r_last_txn_pre_one -> r_last_txn_post_last [ label="BTF\nlen==1" ] + r_more_txn_pre_one -> r_more_txn_post_last [ label="BTF\nlen==1" ] + + r_last_txn_pre_first -> r_last_txn_pre_last [ label="BTF\nlen==2" ] + r_more_txn_pre_first -> r_more_txn_pre_last [ label="BTF\nlen==2" ] + + r_last_txn_pre_first -> r_last_txn_pre_middle [ label="BTF\nlen>2" ] + r_more_txn_pre_first -> r_more_txn_pre_middle [ label="BTF\nlen>2" ] + + r_last_txn_pre_middle -> r_last_txn_pre_middle [ label="BTF\nlen>2" ] + r_more_txn_pre_middle -> r_more_txn_pre_middle [ label="BTF\nlen>2" ] + + r_last_txn_pre_middle -> r_last_txn_pre_last [ label="BTF\nlen==2" ] + r_more_txn_pre_middle -> r_more_txn_pre_last [ label="BTF\nlen==2" ] + + r_last_txn_pre_last -> r_last_txn_post_last [ label="BTF\nlen==1" ] + r_more_txn_pre_last -> r_more_txn_post_last [ label="BTF\nlen==1" ] + + r_last_txn_post_last -> stopping [ label="auto" ] + r_more_txn_post_last -> starting [ label="auto" ] +} diff --git a/flight/Doc/Architecture/stm32_i2c_fsm.jpg b/flight/Doc/Architecture/stm32_i2c_fsm.jpg new file mode 100644 index 000000000..cd208976c Binary files /dev/null and b/flight/Doc/Architecture/stm32_i2c_fsm.jpg differ diff --git a/flight/OpenPilot/System/inc/pios_board.h b/flight/OpenPilot/System/inc/pios_board.h index e5fb4a5aa..1a3f7cb94 100644 --- a/flight/OpenPilot/System/inc/pios_board.h +++ b/flight/OpenPilot/System/inc/pios_board.h @@ -81,21 +81,9 @@ TIM8 | Servo 5 | Servo 6 | Servo 7 | Servo 8 //------------------------ // PIOS_I2C +// See also pios_board.c //------------------------ -#define PIOS_I2C_PORT I2C2 -#define PIOS_I2C_CLK RCC_APB1Periph_I2C2 -#define PIOS_I2C_GPIO_PORT GPIOB -#define PIOS_I2C_SDA_PIN GPIO_Pin_11 -#define PIOS_I2C_SCL_PIN GPIO_Pin_10 -#define PIOS_I2C_DUTY_CYCLE I2C_DutyCycle_2 -#define PIOS_I2C_BUS_FREQ 400000 // bit/s -#define PIOS_I2C_TIMEOUT_VALUE 50 // ms -#define PIOS_I2C_IRQ_EV_HANDLER void I2C2_EV_IRQHandler(void) -#define PIOS_I2C_IRQ_ER_HANDLER void I2C2_ER_IRQHandler(void) -#define PIOS_I2C_IRQ_EV_CHANNEL I2C2_EV_IRQn -#define PIOS_I2C_IRQ_ER_CHANNEL I2C2_ER_IRQn -#define PIOS_I2C_IRQ_EV_PRIORITY PIOS_IRQ_PRIO_HIGH -#define PIOS_I2C_IRQ_ER_PRIORITY PIOS_IRQ_PRIO_HIGH +#define PIOS_I2C_MAIN_ADAPTER 0 //------------------------ // PIOS_BMP085 diff --git a/flight/OpenPilot/System/pios_board.c b/flight/OpenPilot/System/pios_board.c index 679e6486f..623958ed5 100644 --- a/flight/OpenPilot/System/pios_board.c +++ b/flight/OpenPilot/System/pios_board.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -327,7 +328,7 @@ const struct pios_usart_cfg pios_usart_telem_cfg = { .handler = PIOS_USART_telem_irq_handler, .init = { .NVIC_IRQChannel = USART2_IRQn, - .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGH, .NVIC_IRQChannelSubPriority = 0, .NVIC_IRQChannelCmd = ENABLE, }, @@ -575,6 +576,87 @@ struct pios_com_dev pios_com_devs[] = { const uint8_t pios_com_num_devices = NELEMENTS(pios_com_devs); +/* + * I2C Adapters + */ + +void PIOS_I2C_main_adapter_ev_irq_handler(void); +void PIOS_I2C_main_adapter_er_irq_handler(void); +void I2C2_EV_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_ev_irq_handler"))); +void I2C2_ER_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_er_irq_handler"))); + +const struct pios_i2c_adapter_cfg pios_i2c_main_adapter_cfg = { + .regs = I2C2, + .init = { + .I2C_Mode = I2C_Mode_I2C, + .I2C_OwnAddress1 = 0, + .I2C_Ack = I2C_Ack_Enable, + .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, + .I2C_DutyCycle = I2C_DutyCycle_2, + .I2C_ClockSpeed = 400000, /* bits/s */ + }, + .transfer_timeout_ms = 50, + .scl = { + .gpio = GPIOB, + .init = { + .GPIO_Pin = GPIO_Pin_10, + .GPIO_Speed = GPIO_Speed_10MHz, + .GPIO_Mode = GPIO_Mode_AF_OD, + }, + }, + .sda = { + .gpio = GPIOB, + .init = { + .GPIO_Pin = GPIO_Pin_11, + .GPIO_Speed = GPIO_Speed_10MHz, + .GPIO_Mode = GPIO_Mode_AF_OD, + }, + }, + .event = { + .handler = PIOS_I2C_main_adapter_ev_irq_handler, + .flags = 0, /* FIXME: check this */ + .init = { + .NVIC_IRQChannel = I2C2_EV_IRQn, + .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, + .error = { + .handler = PIOS_I2C_main_adapter_er_irq_handler, + .flags = 0, /* FIXME: check this */ + .init = { + .NVIC_IRQChannel = I2C2_ER_IRQn, + .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, +}; + +/* + * Board specific number of devices. + */ +struct pios_i2c_adapter pios_i2c_adapters[] = { + { + .cfg = &pios_i2c_main_adapter_cfg, + }, +}; + +uint8_t pios_i2c_num_adapters = NELEMENTS(pios_i2c_adapters); + +void PIOS_I2C_main_adapter_ev_irq_handler(void) +{ + /* Call into the generic code to handle the IRQ for this specific device */ + PIOS_I2C_EV_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER); +} + +void PIOS_I2C_main_adapter_er_irq_handler(void) +{ + /* Call into the generic code to handle the IRQ for this specific device */ + PIOS_I2C_ER_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER); +} + /** * @} */ diff --git a/flight/PiOS/Common/pios_bmp085.c b/flight/PiOS/Common/pios_bmp085.c index 850e4d919..aecf8ab37 100644 --- a/flight/PiOS/Common/pios_bmp085.c +++ b/flight/PiOS/Common/pios_bmp085.c @@ -145,7 +145,7 @@ void PIOS_BMP085_ReadADC(void) /* Read and store the 16bit result */ if(CurrentRead == TemperatureConv) { /* Read the temperature conversion */ - PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 2); + PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 3); /* Read 3 since it is more reliable on i2c than the proper read of 2 bytes. */ RawTemperature = ((Data[0] << 8) | Data[1]); X1 = (RawTemperature - CalibData.AC6) * CalibData.AC5 >> 15; @@ -196,30 +196,28 @@ int32_t PIOS_BMP085_GetPressure(void) * \return -2 if BMP085 blocked by another task (retry it!) * \return -4 if invalid length */ -int32_t PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len) +bool PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len) { - /* Try to get the I2C peripheral */ - if(PIOS_I2C_LockDevice(0) == FALSE) - { - /* Request a retry */ - return -2; - } + uint8_t addr_buffer[] = { + address, + }; - /* Send I2C address and EEPROM address */ - /* To avoid issues copy address into temporary buffer */ - uint8_t addr_buffer[1] = {(uint8_t)address}; - int32_t error = PIOS_I2C_Transfer(I2C_Write_WithoutStop, BMP085_I2C_ADDR, addr_buffer, 1); + const struct pios_i2c_txn txn_list[] = { + { + .addr = BMP085_I2C_ADDR, + .rw = PIOS_I2C_TXN_WRITE, + .len = sizeof(addr_buffer), + .buf = addr_buffer, + }, + { + .addr = BMP085_I2C_ADDR, + .rw = PIOS_I2C_TXN_READ, + .len = len, + .buf = buffer, + } + }; - /* Now receive byte(s) */ - if(!error) { - error = PIOS_I2C_Transfer(I2C_Read, BMP085_I2C_ADDR, buffer, len); - } - - /* Release I2C peripheral */ - PIOS_I2C_UnlockDevice(); - - /* Return error status */ - return error < 0 ? -1 : 0; + return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list)); } @@ -231,27 +229,23 @@ int32_t PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len) * \return -1 if error during I2C transfer * \return -2 if BMP085 blocked by another task (retry it!) */ -int32_t PIOS_BMP085_Write(uint8_t address, uint8_t buffer) +bool PIOS_BMP085_Write(uint8_t address, uint8_t buffer) { - /* Try to get the IIC peripheral */ - if(PIOS_I2C_LockDevice(0) == FALSE) - { - /* Request a retry */ - return -2; - } + uint8_t data[] = { + address, + buffer, + }; - /* Send I2C address and data */ - uint8_t WriteBuffer[2]; - WriteBuffer[0] = address; - WriteBuffer[1] = buffer; - - int32_t error = PIOS_I2C_Transfer(I2C_Write, BMP085_I2C_ADDR, WriteBuffer, 2); - - /* Release I2C peripheral */ - PIOS_I2C_UnlockDevice(); - - /* Return error status */ - return error < 0 ? -1 : 0; + const struct pios_i2c_txn txn_list[] = { + { + .addr = BMP085_I2C_ADDR, + .rw = PIOS_I2C_TXN_WRITE, + .len = sizeof(data), + .buf = data, + }, + }; + + return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list)); } diff --git a/flight/PiOS/Common/pios_hmc5843.c b/flight/PiOS/Common/pios_hmc5843.c index 522e8b916..18bfc7f98 100644 --- a/flight/PiOS/Common/pios_hmc5843.c +++ b/flight/PiOS/Common/pios_hmc5843.c @@ -206,20 +206,28 @@ void PIOS_HMC5843_ReadID(uint8_t out[4]) * \return -1 if error during I2C transfer * \return -4 if invalid length */ -int32_t PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len) +bool PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len) { - /* Send I2C address and register address */ - /* To avoid issues copy address into temporary buffer */ - uint8_t addr_buffer[1] = {(uint8_t)address}; - int32_t error = PIOS_I2C_Transfer(I2C_Write_WithoutStop, PIOS_HMC5843_I2C_ADDR, addr_buffer, 1); - - /* Now receive byte(s) */ - if(!error) { - error = PIOS_I2C_Transfer(I2C_Read, PIOS_HMC5843_I2C_ADDR, buffer, len); - } - - /* Return error status */ - return error < 0 ? -1 : 0; + uint8_t addr_buffer[] = { + address, + }; + + const struct pios_i2c_txn txn_list[] = { + { + .addr = PIOS_HMC5843_I2C_ADDR, + .rw = PIOS_I2C_TXN_WRITE, + .len = sizeof(addr_buffer), + .buf = addr_buffer, + }, + { + .addr = PIOS_HMC5843_I2C_ADDR, + .rw = PIOS_I2C_TXN_READ, + .len = len, + .buf = buffer, + } + }; + + return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list)); } @@ -230,17 +238,23 @@ int32_t PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len) * \return 0 if operation was successful * \return -1 if error during I2C transfer */ -int32_t PIOS_HMC5843_Write(uint8_t address, uint8_t buffer) +bool PIOS_HMC5843_Write(uint8_t address, uint8_t buffer) { - /* Send I2C address and data */ - uint8_t WriteBuffer[2]; - WriteBuffer[0] = address; - WriteBuffer[1] = buffer; - - int32_t error = PIOS_I2C_Transfer(I2C_Write, PIOS_HMC5843_I2C_ADDR, WriteBuffer, 2); - - /* Return error status */ - return error < 0 ? -1 : 0; + uint8_t data[] = { + address, + buffer, + }; + + const struct pios_i2c_txn txn_list[] = { + { + .addr = PIOS_HMC5843_I2C_ADDR, + .rw = PIOS_I2C_TXN_WRITE, + .len = sizeof(data), + .buf = data, + }, + }; + + return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list)); } #endif diff --git a/flight/PiOS/STM32F10x/pios_i2c.c b/flight/PiOS/STM32F10x/pios_i2c.c index 62842d69d..fd98fc465 100644 --- a/flight/PiOS/STM32F10x/pios_i2c.c +++ b/flight/PiOS/STM32F10x/pios_i2c.c @@ -37,81 +37,521 @@ #if defined(PIOS_INCLUDE_I2C) - - -/* Options */ -//#define USE_DEBUG_PINS - -#ifdef PIOS_INCLUDE_FREERTOS - #define USE_FREERTOS +#if defined(PIOS_INCLUDE_FREERTOS) +#define USE_FREERTOS #endif +#include -/* Global Variables */ -volatile uint32_t PIOS_I2C_UnexpectedEvent; +enum i2c_adapter_event { + I2C_EVENT_START, + I2C_EVENT_STARTED_MORE_TXN_READ, + I2C_EVENT_STARTED_MORE_TXN_WRITE, + I2C_EVENT_STARTED_LAST_TXN_READ, + I2C_EVENT_STARTED_LAST_TXN_WRITE, + I2C_EVENT_ADDR_SENT_LEN_EQ_0, + I2C_EVENT_ADDR_SENT_LEN_EQ_1, + I2C_EVENT_ADDR_SENT_LEN_EQ_2, + I2C_EVENT_ADDR_SENT_LEN_GT_2, + I2C_EVENT_TRANSFER_DONE_LEN_EQ_0, + I2C_EVENT_TRANSFER_DONE_LEN_EQ_1, + I2C_EVENT_TRANSFER_DONE_LEN_EQ_2, + I2C_EVENT_TRANSFER_DONE_LEN_GT_2, + I2C_EVENT_AUTO, /* FIXME: remove this */ -/* Local types */ -typedef union { - struct { - unsigned ALL:8; - }; - struct { - unsigned BUSY:1; - unsigned STOP_REQUESTED:1; - unsigned ABORT_IF_FIRST_BYTE_0:1; - unsigned WRITE_WITHOUT_STOP:1; - unsigned INIRQ:1; - }; -} TransferStateTypeDef; + I2C_EVENT_NUM_EVENTS /* Must be last */ +}; + +static void go_faulted (struct pios_i2c_adapter * i2c_adapter); +static void go_stopping (struct pios_i2c_adapter * i2c_adapter); +static void go_stopped (struct pios_i2c_adapter * i2c_adapter); +static void go_starting (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_addr (struct pios_i2c_adapter * i2c_adapter); +static void go_r_more_txn_pre_one (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_pre_first (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_pre_middle (struct pios_i2c_adapter * i2c_adapter); +static void go_r_more_txn_pre_last (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_post_last (struct pios_i2c_adapter * i2c_adapter); + +static void go_r_any_txn_addr (struct pios_i2c_adapter * i2c_adapter); +static void go_r_last_txn_pre_one (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_pre_first (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_pre_middle (struct pios_i2c_adapter * i2c_adapter); +static void go_r_last_txn_pre_last (struct pios_i2c_adapter * i2c_adapter); +static void go_r_any_txn_post_last (struct pios_i2c_adapter * i2c_adapter); + +static void go_w_any_txn_addr (struct pios_i2c_adapter * i2c_adapter); +static void go_w_any_txn_middle (struct pios_i2c_adapter * i2c_adapter); +static void go_w_more_txn_last (struct pios_i2c_adapter * i2c_adapter); + +static void go_w_any_txn_addr (struct pios_i2c_adapter * i2c_adapter); +static void go_w_any_txn_middle (struct pios_i2c_adapter * i2c_adapter); +static void go_w_last_txn_last (struct pios_i2c_adapter * i2c_adapter); + +struct i2c_adapter_transition { + void (*entry_fn)(struct pios_i2c_adapter * i2c_adapter); + enum i2c_adapter_state next_state[I2C_EVENT_NUM_EVENTS]; +}; + +static void i2c_adapter_inject_event(struct pios_i2c_adapter * i2c_adapter, enum i2c_adapter_event event); +static void i2c_adapter_fsm_init(struct pios_i2c_adapter * i2c_adapter); + +const static struct i2c_adapter_transition i2c_adapter_transitions[I2C_STATE_NUM_STATES] = { + [I2C_STATE_FAULTED] = { + .entry_fn = go_faulted, + }, + + [I2C_STATE_STOPPED] = { + .entry_fn = go_stopped, + .next_state = { + [I2C_EVENT_START] = I2C_STATE_STARTING, + }, + }, + + [I2C_STATE_STOPPING] = { + .entry_fn = go_stopping, + .next_state = { + [I2C_EVENT_AUTO] = I2C_STATE_STOPPED, + }, + }, + + [I2C_STATE_STARTING] = { + .entry_fn = go_starting, + .next_state = { + [I2C_EVENT_STARTED_MORE_TXN_READ] = I2C_STATE_R_MORE_TXN_ADDR, + [I2C_EVENT_STARTED_MORE_TXN_WRITE] = I2C_STATE_W_MORE_TXN_ADDR, + [I2C_EVENT_STARTED_LAST_TXN_READ] = I2C_STATE_R_LAST_TXN_ADDR, + [I2C_EVENT_STARTED_LAST_TXN_WRITE] = I2C_STATE_W_LAST_TXN_ADDR, + }, + }, + + /* + * Read with restart + */ + + [I2C_STATE_R_MORE_TXN_ADDR] = { + .entry_fn = go_r_any_txn_addr, + .next_state = { + [I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_PRE_ONE, + [I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST, + [I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_FIRST, + }, + }, + + [I2C_STATE_R_MORE_TXN_PRE_ONE] = { + .entry_fn = go_r_more_txn_pre_one, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_POST_LAST, + }, + }, + + [I2C_STATE_R_MORE_TXN_PRE_FIRST] = { + .entry_fn = go_r_any_txn_pre_first, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST, + [I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_MIDDLE, + }, + }, + + [I2C_STATE_R_MORE_TXN_PRE_MIDDLE] = { + .entry_fn = go_r_any_txn_pre_middle, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST, + [I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_MIDDLE, + }, + }, + + [I2C_STATE_R_MORE_TXN_PRE_LAST] = { + .entry_fn = go_r_more_txn_pre_last, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_POST_LAST, + }, + }, + + [I2C_STATE_R_MORE_TXN_POST_LAST] = { + .entry_fn = go_r_any_txn_post_last, + .next_state = { + [I2C_EVENT_AUTO] = I2C_STATE_STARTING, + }, + }, + + /* + * Read + */ + + [I2C_STATE_R_LAST_TXN_ADDR] = { + .entry_fn = go_r_any_txn_addr, + .next_state = { + [I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_PRE_ONE, + [I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_FIRST, + [I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_FIRST, + }, + }, + + [I2C_STATE_R_LAST_TXN_PRE_ONE] = { + .entry_fn = go_r_last_txn_pre_one, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_POST_LAST, + }, + }, + + [I2C_STATE_R_LAST_TXN_PRE_FIRST] = { + .entry_fn = go_r_any_txn_pre_first, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_LAST, + [I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_MIDDLE, + }, + }, + + [I2C_STATE_R_LAST_TXN_PRE_MIDDLE] = { + .entry_fn = go_r_any_txn_pre_middle, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_LAST, + [I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_MIDDLE, + }, + }, + + [I2C_STATE_R_LAST_TXN_PRE_LAST] = { + .entry_fn = go_r_last_txn_pre_last, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_POST_LAST, + }, + }, + + [I2C_STATE_R_LAST_TXN_POST_LAST] = { + .entry_fn = go_r_any_txn_post_last, + .next_state = { + [I2C_EVENT_AUTO] = I2C_STATE_STOPPING, + }, + }, -typedef struct { - I2C_TypeDef *base; + /* + * Write with restart + */ - uint8_t i2c_address; - uint8_t *tx_buffer_ptr; - uint8_t *rx_buffer_ptr; - volatile uint16_t buffer_len; - volatile uint16_t buffer_ix; + [I2C_STATE_W_MORE_TXN_ADDR] = { + .entry_fn = go_w_any_txn_addr, + .next_state = { + [I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_W_MORE_TXN_LAST, + [I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_W_MORE_TXN_MIDDLE, + [I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_W_MORE_TXN_MIDDLE, + }, + }, - volatile TransferStateTypeDef transfer_state; - volatile int32_t transfer_error; + [I2C_STATE_W_MORE_TXN_MIDDLE] = { + .entry_fn = go_w_any_txn_middle, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_W_MORE_TXN_LAST, + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_W_MORE_TXN_MIDDLE, + [I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_W_MORE_TXN_MIDDLE, + }, + }, -#ifdef USE_FREERTOS - xSemaphoreHandle sem_readySignal; - portBASE_TYPE xHigherPriorityTaskWoken; - xSemaphoreHandle xBusyMutex; + [I2C_STATE_W_MORE_TXN_LAST] = { + .entry_fn = go_w_more_txn_last, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_0] = I2C_STATE_STARTING, + }, + }, + + /* + * Write + */ + + [I2C_STATE_W_LAST_TXN_ADDR] = { + .entry_fn = go_w_any_txn_addr, + .next_state = { + [I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_W_LAST_TXN_LAST, + [I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_W_LAST_TXN_MIDDLE, + [I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_W_LAST_TXN_MIDDLE, + }, + }, + + [I2C_STATE_W_LAST_TXN_MIDDLE] = { + .entry_fn = go_w_any_txn_middle, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_W_LAST_TXN_LAST, + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_W_LAST_TXN_MIDDLE, + [I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_W_LAST_TXN_MIDDLE, + }, + }, + + [I2C_STATE_W_LAST_TXN_LAST] = { + .entry_fn = go_w_last_txn_last, + .next_state = { + [I2C_EVENT_TRANSFER_DONE_LEN_EQ_0] = I2C_STATE_STOPPING, + }, + }, +}; + +static void go_faulted (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(0); +} + +static void go_stopping (struct pios_i2c_adapter * i2c_adapter) +{ + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); + + /* Spin waiting for the stop bit to be cleared before continuing */ +#define I2C_CR1_STOP_REQUESTED 0x0200 + while (i2c_adapter->cfg->regs->CR1 & I2C_CR1_STOP_REQUESTED); +} + +static void go_stopped (struct pios_i2c_adapter * i2c_adapter) +{ +#ifdef USE_FREERTOS + signed portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; #endif + + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); + I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, ENABLE); -} I2CRecTypeDef; +#ifdef USE_FREERTOS + if (xSemaphoreGiveFromISR(i2c_adapter->sem_ready, &pxHigherPriorityTaskWoken) != pdTRUE) { + PIOS_DEBUG_Assert(0); + } + portEND_SWITCHING_ISR(pxHigherPriorityTaskWoken); /* FIXME: is this the right place for this? */ +#endif /* USE_FREERTOS */ +} -/* Local Prototypes */ -static void PIOS_I2C_InitPeripheral(void); -static void EV_IRQHandler(I2CRecTypeDef *i2cx); -static void ER_IRQHandler(I2CRecTypeDef *i2cx); +static void go_starting (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); -/* Local Variables */ -static I2CRecTypeDef I2CRec; + i2c_adapter->active_byte = &(i2c_adapter->active_txn->buf[0]); + i2c_adapter->last_byte = &(i2c_adapter->active_txn->buf[i2c_adapter->active_txn->len - 1]); -/* Local Functions */ -static void TransferStart(I2CRecTypeDef *i2cx); -static void TransferEnd(I2CRecTypeDef *i2cx); + I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE); + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE); +} -/* Macros */ -#ifdef USE_DEBUG_PINS - #define DEBUG_PIN_ISR 0 - #define DEBUG_PIN_BUSY 1 - #define DEBUG_PIN_WAIT 2 - #define DEBUG_PIN_ASSERT 7 - #define DebugPinHigh(x) PIOS_DEBUG_PinHigh(x) - #define DebugPinLow(x) PIOS_DEBUG_PinLow(x) -#else - #define DebugPinHigh(x) - #define DebugPinLow(x) -#endif +/* Common to 'more' and 'last' transaction */ +static void go_r_any_txn_addr (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); -#define Assert(exp) PIOS_DEBUG_Assert(exp) + PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_READ); + I2C_Send7bitAddress(i2c_adapter->cfg->regs, i2c_adapter->active_txn->addr, I2C_Direction_Receiver); +} + +static void go_r_more_txn_pre_one (struct pios_i2c_adapter * i2c_adapter) +{ + I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE); + I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE); +} + +static void go_r_last_txn_pre_one (struct pios_i2c_adapter * i2c_adapter) +{ + I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE); + I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE); +} + + +/* Common to 'more' and 'last' transaction */ +static void go_r_any_txn_pre_first (struct pios_i2c_adapter * i2c_adapter) +{ + I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, ENABLE); +} + +/* Common to 'more' and 'last' transaction */ +static void go_r_any_txn_pre_middle (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); + + *(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs); + + /* Move to the next byte */ + i2c_adapter->active_byte++; + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); +} + +static void go_r_more_txn_pre_last (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); + + I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE); + PIOS_IRQ_Disable(); + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); + I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE); + *(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs); + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE); + PIOS_IRQ_Enable(); + + /* Move to the next byte */ + i2c_adapter->active_byte++; + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); +} + +static void go_r_last_txn_pre_last (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); + + I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE); + PIOS_IRQ_Disable(); + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); + I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE); + *(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs); + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE); + PIOS_IRQ_Enable(); + + /* Move to the next byte */ + i2c_adapter->active_byte++; + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); +} + +/* Common to 'more' and 'last' transaction */ +static void go_r_any_txn_post_last (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); + + *(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs); + + /* Move to the next byte */ + i2c_adapter->active_byte++; + + /* Move to the next transaction */ + i2c_adapter->active_txn++; +} + +/* Common to 'more' and 'last' transaction */ +static void go_w_any_txn_addr (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); + + PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_WRITE); + + I2C_Send7bitAddress(i2c_adapter->cfg->regs, i2c_adapter->active_txn->addr, I2C_Direction_Transmitter); +} + +static void go_w_any_txn_middle (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte < i2c_adapter->last_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); + + I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte)); + + /* Move to the next byte */ + i2c_adapter->active_byte++; + PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte); +} + +static void go_w_more_txn_last (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); + + I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte)); + + /* Move to the next byte */ + i2c_adapter->active_byte++; + + /* Move to the next transaction */ + i2c_adapter->active_txn++; + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); +} + + +static void go_w_last_txn_last (struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_DEBUG_Assert(i2c_adapter->active_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte); + PIOS_DEBUG_Assert(i2c_adapter->active_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn); + PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn); + + I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_BUF, DISABLE); + I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte)); + +// SHOULD MOVE THIS INTO A STOPPING STATE AND SET IT ONLY AFTER THE BYTE WAS SENT + I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE); + + /* Move to the next byte */ + i2c_adapter->active_byte++; +} + +static void i2c_adapter_inject_event(struct pios_i2c_adapter * i2c_adapter, enum i2c_adapter_event event) +{ + PIOS_IRQ_Disable(); + + /* + * Move to the next state + * + * This is done prior to calling the new state's entry function to + * guarantee that the entry function never depends on the previous + * state. This way, it cannot ever know what the previous state was. + */ + enum i2c_adapter_state prev_state = i2c_adapter->curr_state; + if (prev_state); + + i2c_adapter->curr_state = i2c_adapter_transitions[i2c_adapter->curr_state].next_state[event]; + + /* Call the entry function (if any) for the next state. */ + if (i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn) { + i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn(i2c_adapter); + } + PIOS_IRQ_Enable(); +} + +static void i2c_adapter_process_auto(struct pios_i2c_adapter * i2c_adapter) +{ + PIOS_IRQ_Disable(); + + enum i2c_adapter_state prev_state = i2c_adapter->curr_state; + if (prev_state); + + while (i2c_adapter_transitions[i2c_adapter->curr_state].next_state[I2C_EVENT_AUTO]) { + i2c_adapter->curr_state = i2c_adapter_transitions[i2c_adapter->curr_state].next_state[I2C_EVENT_AUTO]; + + /* Call the entry function (if any) for the next state. */ + if (i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn) { + i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn(i2c_adapter); + } + } + + PIOS_IRQ_Enable(); +} + +static void i2c_adapter_fsm_init(struct pios_i2c_adapter * i2c_adapter) +{ + i2c_adapter->curr_state = I2C_STATE_STOPPED; + //go_stopped(i2c_adapter); +} + + +#include + +static struct pios_i2c_adapter * find_i2c_adapter_by_id (uint8_t adapter) +{ + if (adapter >= pios_i2c_num_adapters) { + /* Undefined I2C adapter for this board (see pios_board.c) */ + return NULL; + } + + /* Get a handle for the device configuration */ + return &(pios_i2c_adapters[adapter]); +} /** * Initializes IIC driver @@ -120,556 +560,232 @@ static void TransferEnd(I2CRecTypeDef *i2cx); */ int32_t PIOS_I2C_Init(void) { - /* Configure IIC pins in open drain mode */ - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; - - GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SCL_PIN; - GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SDA_PIN; - GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure); - - PIOS_I2C_InitPeripheral(); + struct pios_i2c_adapter * i2c_adapter; + for (uint8_t i = 0; i < pios_i2c_num_adapters; i++) { + /* Get a handle for the device configuration */ + i2c_adapter = find_i2c_adapter_by_id(i); + PIOS_DEBUG_Assert(i2c_adapter); #ifdef USE_FREERTOS - vSemaphoreCreateBinary(I2CRec.sem_readySignal); - I2CRec.xBusyMutex = xSemaphoreCreateMutex(); + /* + * Must be done prior to calling i2c_adapter_fsm_init() + * since the sem_ready mutex is used in the initial state. + */ + vSemaphoreCreateBinary(i2c_adapter->sem_ready); + i2c_adapter->sem_busy = xSemaphoreCreateMutex(); #endif // USE_FREERTOS - TransferEnd(&I2CRec); + /* Initialize the state machine */ + i2c_adapter_fsm_init(i2c_adapter); - /* Configure and enable I2C interrupts */ - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = PIOS_I2C_IRQ_EV_CHANNEL; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_EV_PRIORITY; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); + /* Initialize the GPIO pins */ + GPIO_Init(i2c_adapter->cfg->scl.gpio, &(i2c_adapter->cfg->scl.init)); + GPIO_Init(i2c_adapter->cfg->sda.gpio, &(i2c_adapter->cfg->sda.init)); - NVIC_InitStructure.NVIC_IRQChannel = PIOS_I2C_IRQ_ER_CHANNEL; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_ER_PRIORITY; - NVIC_Init(&NVIC_InitStructure); + /* Enable the associated peripheral clock */ + switch ((uint32_t)i2c_adapter->cfg->regs) { + case (uint32_t)I2C1: + /* Enable I2C peripheral clock (APB1 == slow speed) */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); + break; + case (uint32_t)I2C2: + /* Enable I2C peripheral clock (APB1 == slow speed) */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); + break; + } - DebugPinLow(2); - - /* No error */ - return 0; -} - - -/** -* Internal function to (re-)initialize the I2C peripheral -*/ -static void PIOS_I2C_InitPeripheral(void) -{ - I2C_InitTypeDef I2C_InitStructure; - I2CRecTypeDef *i2cx = &I2CRec; - - /* Prepare I2C init-struct */ - I2C_StructInit(&I2C_InitStructure); - I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; - I2C_InitStructure.I2C_OwnAddress1 = 0; - I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; - I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; - - /* Define base address */ - i2cx->base = PIOS_I2C_PORT; - - /* enable peripheral clock of I2C */ - RCC_APB1PeriphClockCmd(PIOS_I2C_CLK, ENABLE); - - /* Set I2C clock bus clock params */ - /* Note that the STM32 driver handles value <= 100kHz differently! (duty cycle always 1:1) */ - /* Important: bus frequencies > 400kHz don't work stable */ - I2C_InitStructure.I2C_DutyCycle = PIOS_I2C_DUTY_CYCLE; - I2C_InitStructure.I2C_ClockSpeed = PIOS_I2C_BUS_FREQ; - - /* Trigger software reset via I2C_DeInit */ - I2C_DeInit(i2cx->base); - - /* Clear transfer state and error value */ - i2cx->transfer_state.ALL = 0; - i2cx->transfer_error = 0; - - /* Configure I2C peripheral */ - I2C_Init(i2cx->base, &I2C_InitStructure); -} - - -#ifdef USE_FREERTOS -/** -* Semaphore handling: requests the IIC interface -* \param[in] timeout Timeout in ticks, 0 for no delay -* \return TRUE when the lock to the device was obtained -*/ -bool PIOS_I2C_LockDevice(portTickType timeout) -{ - if (xSemaphoreTake(I2CRec.xBusyMutex, timeout) == pdTRUE) - { - // Ok, got device - return TRUE; - } - else - { - return FALSE; - } -} - - - -/** -* Semaphore handling: releases the IIC interface for other tasks -* \return < 0 on errors -*/ -void PIOS_I2C_UnlockDevice(void) -{ - xSemaphoreGive(I2CRec.xBusyMutex); -} - -#endif // USE_FREERTOS - - -/** - * Internal function called at the start of a transfer - */ -static void TransferStart(I2CRecTypeDef *i2cx) -{ - Assert(i2cx->transfer_state.BUSY == 0); - - DebugPinHigh(DEBUG_PIN_BUSY); - i2cx->transfer_state.BUSY = 1; - - // Enable Interrupts: I2V2 event, buffer and error interrupt - I2C_ITConfig(i2cx->base, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE); -} - -/** - * Internal function called at the end of a transfer - */ -static void TransferEnd(I2CRecTypeDef *i2cx) -{ - // Disable all interrupts - I2C_ITConfig(i2cx->base, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); - - DebugPinLow(DEBUG_PIN_BUSY); - i2cx->transfer_state.BUSY = 0; - -#ifdef USE_FREERTOS - if (i2cx->transfer_state.INIRQ) - { - xSemaphoreGiveFromISR(i2cx->sem_readySignal, &i2cx->xHigherPriorityTaskWoken); - } - else - { - xSemaphoreGive(i2cx->sem_readySignal); - } +#if 0 /* FIXME: is this reset necessary? Should it be done in the stopped state? */ + /* Reset the I2C block */ + I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, ENABLE); + I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, DISABLE); #endif + + /* Reset the I2C block */ + I2C_DeInit(i2c_adapter->cfg->regs); + + /* Initialize the I2C block */ + I2C_Init(i2c_adapter->cfg->regs, &(i2c_adapter->cfg->init)); + +#if 1 /* FIXME: is this reset necessary? Should it be done in the stopped state? */ +#define I2C_BUSY 0x20 + if (i2c_adapter->cfg->regs->SR2 & I2C_BUSY) { + /* Reset the I2C block */ + I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, ENABLE); + I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, DISABLE); + } +#endif + + /* Configure and enable I2C interrupts */ + NVIC_Init(&(i2c_adapter->cfg->event.init)); + NVIC_Init(&(i2c_adapter->cfg->error.init)); + } + + /* No error */ + return 0; } -/** -* Checks if transfer is finished -* \return 1 if ongoing transfer -* \return <=0 no transfer is busy; return value indicates error value of last transfer -* (PIOS_I2C_TransferBegin() has to be called again) -*/ -int32_t PIOS_I2C_TransferCheck(void) + +bool PIOS_I2C_Transfer(uint8_t i2c, const struct pios_i2c_txn txn_list[], uint32_t num_txns) { - I2CRecTypeDef *i2cx = &I2CRec; + PIOS_DEBUG_Assert(txn_list); + PIOS_DEBUG_Assert(num_txns); - if(i2cx->transfer_state.BUSY) - return 1; + struct pios_i2c_adapter * i2c_adapter; - return i2cx->transfer_error; -} - -/** - * Stop the current transfer - * \param error the error that must be reported - */ -void PIOS_I2C_TerminateTransfer(uint32_t error) -{ - I2CRecTypeDef *i2cx = &I2CRec; - - /* Send stop condition */ - I2C_GenerateSTOP(i2cx->base, ENABLE); - - /* Re-initialize peripheral */ - PIOS_I2C_InitPeripheral(); - - i2cx->transfer_error = error; -} - -/** -* Waits until transfer is finished. -* \return error value of the transfer -*/ -int32_t PIOS_I2C_TransferWait(void) -{ - I2CRecTypeDef *i2cx = &I2CRec; - - DebugPinHigh(DEBUG_PIN_WAIT); + i2c_adapter = find_i2c_adapter_by_id(i2c); + PIOS_DEBUG_Assert(i2c_adapter); #ifdef USE_FREERTOS + /* Lock the bus */ + xSemaphoreTake(i2c_adapter->sem_busy, portMAX_DELAY); +#endif /* USE_FREERTOS */ - if (i2cx->transfer_state.BUSY) - { - // Wait until we see the ready signal - if (xSemaphoreTake(i2cx->sem_readySignal, PIOS_I2C_TIMEOUT_VALUE/portTICK_RATE_MS) == pdTRUE) - { - // OK, got the semaphore, release it again - Assert(i2cx->transfer_state.BUSY == 0); - } - else - { - PIOS_I2C_TerminateTransfer(I2C_ERROR_TIMEOUT); - } - } + PIOS_DEBUG_Assert(i2c_adapter->curr_state == I2C_STATE_STOPPED); + i2c_adapter->first_txn = &txn_list[0]; + i2c_adapter->last_txn = &txn_list[num_txns - 1]; + i2c_adapter->active_txn = i2c_adapter->first_txn; + +#ifdef USE_FREERTOS + /* Make sure the done/ready semaphore is consumed before we start */ + xSemaphoreTake(i2c_adapter->sem_ready, portMAX_DELAY); +#endif + + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_START); + + /* Wait for the transfer to complete */ +#ifdef USE_FREERTOS + xSemaphoreTake(i2c_adapter->sem_ready, portMAX_DELAY); + xSemaphoreGive(i2c_adapter->sem_ready); #else - uint32_t repeat_ctr = PIOS_I2C_TIMEOUT_VALUE*100; // FIXME - uint16_t last_buffer_ix = i2cx->buffer_ix; - - if (i2cx->transfer_state.BUSY) - { - while(--repeat_ctr > 0) - { - /* Check if buffer index has changed - if so, reload repeat counter */ - if(i2cx->buffer_ix != last_buffer_ix) { - repeat_ctr = PIOS_I2C_TIMEOUT_VALUE; - last_buffer_ix = i2cx->buffer_ix; - } - - /* Get transfer state */ - int32_t check_state = PIOS_I2C_TransferCheck(); - - /* Exit if transfer finished */ - if(check_state <= 0) - { - DebugPinLow(DEBUG_PIN_WAIT); - return check_state; - } - } - - /* Timeout error - something is stalling... */ - PIOS_I2C_TerminateTransfer(I2C_ERROR_TIMEOUT); - } -#endif - - DebugPinLow(DEBUG_PIN_WAIT); - - return i2cx->transfer_error; -} - -/** -* Perform a transfer. No previous transfer should be ongoing when this function is called. -* When the function returns, the transfer is finished. -* See PIOS_I2C_StartTransfer() for details on the parameters. -* \return 0 no error -* \return < 0 on errors -* -*/ -int32_t PIOS_I2C_Transfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len) -{ - PIOS_I2C_StartTransfer(transfer, address, buffer, len); - return PIOS_I2C_TransferWait(); -} - -/** -* Starts a new transfer. No previous transfer should be ongoing when this function is called. -* When this function returns, the new transfer is ongoing. PIOS_I2C_TransferWait() should be called -* to wait for the transfer to finish and to retrieve the result code. -* \param[in] transfer type:
-*
    -*
  • I2C_Read: a common Read transfer -*
  • I2C_Write: a common Write transfer -*
  • I2C_Write_WithoutStop: don't send stop condition after transfer to allow -* a restart condition (e.g. used to access EEPROMs) -* \param[in] address of I2C device (bit 0 always cleared) -* \param[in] *buffer pointer to transmit/receive buffer -* \param[in] len number of bytes which should be transmitted/received -*/ -void PIOS_I2C_StartTransfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len) -{ - I2CRecTypeDef *i2cx = &I2CRec; - - // Should not be busy - if (i2cx->transfer_state.BUSY) - { - Assert(0); - return; - } + /* Spin waiting for the transfer to finish */ + while (i2c_adapter->curr_state != I2C_STATE_STOPPED); +#endif /* USE_FREERTOS */ #ifdef USE_FREERTOS - // Consume Ready semaphore in case it would be there for some reason - xSemaphoreTake(i2cx->sem_readySignal, 0); - Assert(xSemaphoreTake(i2cx->sem_readySignal, 0) == pdFALSE); -#endif + /* Unlock the bus */ + xSemaphoreGive(i2c_adapter->sem_busy); +#endif /* USE_FREERTOS */ - // Clear state - i2cx->transfer_state.ALL = 0; - i2cx->transfer_error = 0; - - // Set buffer length and start index - i2cx->buffer_len = len; - i2cx->buffer_ix = 0; - - if(transfer == I2C_Read) - { - /* Take new address/buffer/len */ - /* Set bit 0 for read operation */ - i2cx->i2c_address = address | 1; - /* Ensure that previous TX buffer won't be accessed */ - i2cx->tx_buffer_ptr = NULL; - i2cx->rx_buffer_ptr = buffer; - // Ack the bytes we will be getting - I2C_AcknowledgeConfig(i2cx->base, ENABLE); - } - else if(transfer == I2C_Write || transfer == I2C_Write_WithoutStop) - { - /* Take new address/buffer/len */ - /* Clear bit 0 for write operation */ - i2cx->i2c_address = address & 0xfe; - i2cx->tx_buffer_ptr = buffer; - /* Ensure that nothing will be received */ - i2cx->rx_buffer_ptr = NULL; - /* Option to skip stop-condition generation after successful write */ - i2cx->transfer_state.WRITE_WITHOUT_STOP = transfer == I2C_Write_WithoutStop ? 1 : 0; - } - else - { - i2cx->transfer_error = I2C_ERROR_UNSUPPORTED_TRANSFER_TYPE; - return; - } - - // Start the transfer - I2C_GenerateSTART(i2cx->base, ENABLE); - TransferStart(i2cx); -} - -/** -* Internal function for handling IIC event interrupts -*/ -static void EV_IRQHandler(I2CRecTypeDef *i2cx) -{ - uint8_t b; - uint32_t event; - - DebugPinHigh(DEBUG_PIN_ISR); - - // Update state - i2cx->transfer_state.INIRQ = 1; -#ifdef USE_FREERTOS - i2cx->xHigherPriorityTaskWoken = pdFALSE; -#endif - - /* Read SR1 and SR2 at the beginning (if not done so, flags may get lost) */ - event = I2C_GetLastEvent(i2cx->base); - - /* The order of the handling blocks is chosen by test results @ 1MHZ */ - /* Don't change this order */ - - /* RxNE set, will be cleared by reading/writing DR */ - /* Note: also BTF will be reset after a read of SR1 (TxE flag) followed by either read/write DR */ - /* Or a START or STOP condition generated */ - /* Failsave: really requested a receive transfer? If not, continue to check TXE flag, if not set, */ - /* We'll end up in the unexpected event handler. */ - if(event & I2C_FLAG_RXNE && i2cx->rx_buffer_ptr != NULL) { - /* Get received data */ - b = I2C_ReceiveData(i2cx->base); - - /* Failsave: still place in buffer? */ - if(i2cx->buffer_ix < i2cx->buffer_len) { - i2cx->rx_buffer_ptr[i2cx->buffer_ix++] = b; - } - - - /* Last byte received, disable interrupts and return. */ - if(i2cx->transfer_state.STOP_REQUESTED) { - TransferEnd(i2cx); - goto isr_return; - } - - /* Request NAK and stop condition before receiving last data */ - if(i2cx->buffer_ix >= i2cx->buffer_len-1) { - /* Request NAK */ - I2C_AcknowledgeConfig(i2cx->base, DISABLE); - /* Request stop condition */ - I2C_GenerateSTOP(i2cx->base, ENABLE); - i2cx->transfer_state.STOP_REQUESTED = 1; - } - goto isr_return; - } - - /* ADDR set, TRA flag not set (indicates transmitter/receiver mode). */ - /* ADDR will be cleared by a read of SR1 followed by a read of SR2 (done by I2C_GetLastEvent) */ - /* If transmitter mode is selected (TRA set), we go on, TXE will be catched to send the first byte */ - if((event & I2C_FLAG_ADDR) && !(event & I2C_FLAG_TRA)) { - /* Address sent (receiver mode), receiving first byte - check if we already have to request NAK/Stop */ - if(i2cx->buffer_len == 1) { - /* Request NAK */ - I2C_AcknowledgeConfig(i2cx->base, DISABLE); - /* Request stop condition */ - I2C_GenerateSTOP(i2cx->base, ENABLE); - i2cx->transfer_state.STOP_REQUESTED = 1; - } - goto isr_return; - } - - /* TxE set, will be cleared by writing DR, or after START or STOP was generated */ - /* This handling also applies for BTF, as TXE will alway be set if BTF is. */ - /* Note: also BTF will be reset after a read of SR1 (TxE flag) followed by either read/write DR */ - /* Or a START or STOP condition generated */ - if(event & I2C_FLAG_TXE) { - /* Last byte already sent, disable interrupts and return. */ - if(i2cx->transfer_state.STOP_REQUESTED) { - TransferEnd(i2cx); - goto isr_return; - } - - if(i2cx->buffer_ix < i2cx->buffer_len) { - /* Checking tx_buffer_ptr for NULL is a failsafe measure. */ - I2C_SendData(i2cx->base, (i2cx->tx_buffer_ptr == NULL) ? 0 : i2cx->tx_buffer_ptr[i2cx->buffer_ix++]); - goto isr_return; - } - - /* Peripheral is transfering last byte, request stop condition / */ - /* On write-without-stop transfer-type, request start condition instead */ - i2cx->transfer_state.STOP_REQUESTED = 1; - if(!i2cx->transfer_state.WRITE_WITHOUT_STOP) - { - I2C_GenerateSTOP(i2cx->base, ENABLE); - } - else - { - DebugPinHigh(2); - } - - if(i2cx->buffer_len == 0) { - TransferEnd(i2cx); - } else { - /* Disable the I2C_IT_BUF interrupt after sending the last buffer data */ - /* (last EV8) to not allow a new interrupt just with TxE - only BTF will generate it */ - /* If this is not done, BUSY will be cleared before the transfer is finished */ - I2C_ITConfig(i2cx->base, I2C_IT_BUF, DISABLE); - } - goto isr_return; - } - - // Send address - /* SB set, cleared by reading SR1 (done by I2C_GetLastEvent) followed by writing DR register */ - if(event & I2C_FLAG_SB) { - /* Don't send address if stop was requested (WRITE_WITHOUT_STOP - mode, start condition was sent) */ - /* We have to wait for the application to start the next transfer */ - if(i2cx->transfer_state.STOP_REQUESTED) { - TransferEnd(i2cx); - DebugPinLow(DEBUG_PIN_ISR); - goto isr_return; - } - - /* Send IIC address */ - I2C_Send7bitAddress(i2cx->base, i2cx->i2c_address, - (i2cx->i2c_address & 1) - ? I2C_Direction_Receiver - : I2C_Direction_Transmitter); - goto isr_return; - } - - DebugPinHigh(DEBUG_PIN_ASSERT);DebugPinLow(DEBUG_PIN_ASSERT); - - // - // FredericG: Despite the comments below, it seems to me that this situation can happen and can - // be ignored without ill effects... - // For now this condition does not stop the transfer, but further investigation in needed - // - -// Assert(0); -// -// /* This code is only reached if something got wrong, e.g. interrupt handler is called too late, */ -// /* The device reset itself (while testing, it was always event 0x00000000). we have to stop the transfer, */ -// /* Else read/write of corrupt data may be the result. */ -// -// /* Notify error */ -// PIOS_I2C_UnexpectedEvent = event; -// i2cx->transfer_error = I2C_ERROR_UNEXPECTED_EVENT; -// -// TransferEnd(i2cx); -// -// /* Do dummy read to send NAK + STOP condition */ -// I2C_AcknowledgeConfig(i2cx->base, DISABLE); -// b = I2C_ReceiveData(i2cx->base); -// I2C_GenerateSTOP(i2cx->base, ENABLE); - -isr_return: - // Cause task-switch when needed -#ifdef USE_FREERTOS - portEND_SWITCHING_ISR(i2cx->xHigherPriorityTaskWoken); -#endif - - // Update state - i2cx->transfer_state.INIRQ = 0; - DebugPinLow(DEBUG_PIN_ISR); -} - - -/** -* Internal function for handling IIC error interrupts -*/ -static void ER_IRQHandler(I2CRecTypeDef *i2cx) -{ - /* Read SR1 and SR2 at the beginning (if not done so, flags may get lost) */ - uint32_t event = I2C_GetLastEvent(i2cx->base); - - i2cx->transfer_state.INIRQ = 1; - - /* Note that only one error number is available */ - /* The order of these checks defines the priority */ - - /* Bus error (start/stop condition during read */ - /* Unlikely, should only be relevant for slave mode?) */ - if(event & I2C_FLAG_BERR) { - I2C_ClearITPendingBit(i2cx->base, I2C_IT_BERR); - i2cx->transfer_error = I2C_ERROR_BUS; - } - - /* Arbitration lost */ - if(event & I2C_FLAG_ARLO) { - I2C_ClearITPendingBit(i2cx->base, I2C_IT_ARLO); - i2cx->transfer_error = I2C_ERROR_ARBITRATION_LOST; - } - - /* No acknowledge received from slave (e.g. slave not connected) */ - if(event & I2C_FLAG_AF) { - I2C_ClearITPendingBit(i2cx->base, I2C_IT_AF); - i2cx->transfer_error = I2C_ERROR_SLAVE_NOT_CONNECTED; - /* Send stop condition to release bus */ - I2C_GenerateSTOP(i2cx->base, ENABLE); - } - - /* Notify that transfer has finished (due to the error) */ - TransferEnd(i2cx); - - i2cx->transfer_state.INIRQ = 0; -} - - -/* Interrupt vectors */ -PIOS_I2C_IRQ_EV_HANDLER -{ - EV_IRQHandler((I2CRecTypeDef *)&I2CRec); -} - -PIOS_I2C_IRQ_ER_HANDLER -{ - ER_IRQHandler((I2CRecTypeDef *)&I2CRec); + return TRUE; } #endif +void PIOS_I2C_EV_IRQ_Handler(uint8_t i2c) +{ + struct pios_i2c_adapter * i2c_adapter; + + i2c_adapter = find_i2c_adapter_by_id(i2c); + PIOS_DEBUG_Assert(i2c_adapter); + + uint32_t event = I2C_GetLastEvent(i2c_adapter->cfg->regs); + + switch (event) { + case (I2C_EVENT_MASTER_MODE_SELECT | 0x40): + /* Unexplained event: EV5 + RxNE : Extraneous Rx. Probably a late NACK from previous read. */ + /* Clean up the extra Rx until the root cause is identified and just keep going */ + (void) I2C_ReceiveData(i2c_adapter->cfg->regs); + /* Fall through */ + case I2C_EVENT_MASTER_MODE_SELECT: /* EV5 */ + switch (i2c_adapter->active_txn->rw) { + case PIOS_I2C_TXN_READ: + if (i2c_adapter->active_txn == i2c_adapter->last_txn) { + /* Final transaction */ + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_LAST_TXN_READ); + } else if (i2c_adapter->active_txn < i2c_adapter->last_txn) { + /* More transactions follow */ + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_MORE_TXN_READ); + } else { + PIOS_DEBUG_Assert(0); + } + break; + case PIOS_I2C_TXN_WRITE: + if (i2c_adapter->active_txn == i2c_adapter->last_txn) { + /* Final transaction */ + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_LAST_TXN_WRITE); + } else if (i2c_adapter->active_txn < i2c_adapter->last_txn) { + /* More transactions follow */ + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_MORE_TXN_WRITE); + } else { + PIOS_DEBUG_Assert(0); + } + break; + default: + PIOS_DEBUG_Assert(0); + break; + } + break; + case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: /* EV6 */ + case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: /* EV6 */ + switch (i2c_adapter->last_byte - i2c_adapter->active_byte + 1) { + case 0: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_0); + break; + case 1: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_1); + break; + case 2: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_2); + break; + default: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_GT_2); + break; + } + break; + case 0x80: /* TxE only. TRA + MSL + BUSY have been cleared before we got here. */ + /* Ignore */ + { + static volatile bool halt = FALSE; + while (halt); + } + break; + case 0: /* Unexplained spurious event. Not sure what to do here. */ + case 0x40: /* RxNE only. MSL + BUSY have already been cleared by HW. */ + case 0x44: /* RxNE + BTF. MSL + BUSY have already been cleared by HW. */ + case I2C_EVENT_MASTER_BYTE_RECEIVED: /* EV7 */ + case (I2C_EVENT_MASTER_BYTE_RECEIVED | 0x4): /* EV7 + BTF */ + case I2C_EVENT_MASTER_BYTE_TRANSMITTED: /* EV8_2 */ + case 0x84: /* TxE + BTF. EV8_2 but TRA + MSL + BUSY have already been cleared by HW. */ + switch (i2c_adapter->last_byte - i2c_adapter->active_byte + 1) { + case 0: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_0); + break; + case 1: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_1); + break; + case 2: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_2); + break; + default: + i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_GT_2); + break; + } + break; + case I2C_EVENT_MASTER_BYTE_TRANSMITTING: /* EV8 */ + /* Ignore this event and wait for TRANSMITTED in case we can't keep up */ + break; + default: + PIOS_DEBUG_Assert(0); + break; + } + + i2c_adapter_process_auto(i2c_adapter); +} + +void PIOS_I2C_ER_IRQ_Handler(uint8_t i2c) +{ + struct pios_i2c_adapter * i2c_adapter; + + i2c_adapter = find_i2c_adapter_by_id(i2c); + PIOS_DEBUG_Assert(i2c_adapter); + + /* Fail hard on any errors for now */ + PIOS_DEBUG_Assert(0); +} + /** * @} * @} - */ \ No newline at end of file + */ diff --git a/flight/PiOS/inc/pios_bmp085.h b/flight/PiOS/inc/pios_bmp085.h index bd5948bf3..4d2d75012 100644 --- a/flight/PiOS/inc/pios_bmp085.h +++ b/flight/PiOS/inc/pios_bmp085.h @@ -75,12 +75,12 @@ extern void PIOS_BMP085_StartADC(ConversionTypeTypeDef Type); extern void PIOS_BMP085_ReadADC(void); extern int16_t PIOS_BMP085_GetTemperature(void); extern int32_t PIOS_BMP085_GetPressure(void); -extern int32_t PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len); -extern int32_t PIOS_BMP085_Write(uint8_t address, uint8_t buffer); +extern bool PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len); +extern bool PIOS_BMP085_Write(uint8_t address, uint8_t buffer); #endif /* PIOS_BMP085_H */ /** * @} * @} - */ \ No newline at end of file + */ diff --git a/flight/PiOS/inc/pios_hmc5843.h b/flight/PiOS/inc/pios_hmc5843.h index 4acfd4359..545ce68ae 100644 --- a/flight/PiOS/inc/pios_hmc5843.h +++ b/flight/PiOS/inc/pios_hmc5843.h @@ -103,12 +103,12 @@ extern void PIOS_HMC5843_Init(void); extern void PIOS_HMC5843_Config(PIOS_HMC5843_ConfigTypeDef *HMC5843_Config_Struct); extern void PIOS_HMC5843_ReadMag(int16_t out[3]); extern void PIOS_HMC5843_ReadID(uint8_t out[4]); -extern int32_t PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len); -extern int32_t PIOS_HMC5843_Write(uint8_t address, uint8_t buffer); +extern bool PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len); +extern bool PIOS_HMC5843_Write(uint8_t address, uint8_t buffer); #endif /* PIOS_HMC5843_H */ /** * @} * @} - */ \ No newline at end of file + */ diff --git a/flight/PiOS/inc/pios_i2c.h b/flight/PiOS/inc/pios_i2c.h index 929ca2194..b884d0b62 100644 --- a/flight/PiOS/inc/pios_i2c.h +++ b/flight/PiOS/inc/pios_i2c.h @@ -31,42 +31,28 @@ #ifndef PIOS_I2C_H #define PIOS_I2C_H -/* Local defines */ -#define I2C_ERROR_INVALID_PORT -1 -#define I2C_ERROR_GENERAL -2 -#define I2C_ERROR_UNSUPPORTED_TRANSFER_TYPE -3 -#define I2C_ERROR_TIMEOUT -4 -#define I2C_ERROR_ARBITRATION_LOST -5 -#define I2C_ERROR_BUS -6 -#define I2C_ERROR_SLAVE_NOT_CONNECTED -7 -#define I2C_ERROR_UNEXPECTED_EVENT -8 -#define I2C_ERROR_RX_BUFFER_OVERRUN -9 -#define I2C_ERROR_BUSY -10 - - /* Global Types */ +enum pios_i2c_txn_direction { + PIOS_I2C_TXN_READ, + PIOS_I2C_TXN_WRITE +}; -typedef enum { - I2C_Read, - I2C_Write, - I2C_Write_WithoutStop -} I2CTransferTypeDef; +struct pios_i2c_txn { + uint16_t addr; + enum pios_i2c_txn_direction rw; + uint32_t len; + uint8_t * buf; +}; /* Public Functions */ extern int32_t PIOS_I2C_Init(void); -extern int32_t PIOS_I2C_TransferCheck(void); -extern int32_t PIOS_I2C_TransferWait(void); -extern int32_t PIOS_I2C_Transfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len); -extern void PIOS_I2C_StartTransfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len); - -#ifdef PIOS_INCLUDE_FREERTOS -extern bool PIOS_I2C_LockDevice(portTickType timeout); -extern void PIOS_I2C_UnlockDevice(void); -#endif // PIOS_INCLUDE_FREERTOS +extern bool PIOS_I2C_Transfer(uint8_t i2c, const struct pios_i2c_txn txn_list[], uint32_t num_txns); +extern void PIOS_I2C_EV_IRQ_Handler(uint8_t i2c); +extern void PIOS_I2C_ER_IRQ_Handler(uint8_t i2c); #endif /* PIOS_I2C_H */ /** * @} * @} - */ \ No newline at end of file + */ diff --git a/flight/PiOS/inc/pios_i2c_priv.h b/flight/PiOS/inc/pios_i2c_priv.h new file mode 100644 index 000000000..660e92ae3 --- /dev/null +++ b/flight/PiOS/inc/pios_i2c_priv.h @@ -0,0 +1,96 @@ +/** + ****************************************************************************** + * + * @file pios_i2c_priv.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief I2C private definitions. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_I2C_PRIV_H +#define PIOS_I2C_PRIV_H + +#include +#include + +struct pios_i2c_adapter_cfg { + I2C_TypeDef * regs; + I2C_InitTypeDef init; + + uint32_t transfer_timeout_ms; + struct stm32_gpio scl; + struct stm32_gpio sda; + struct stm32_irq event; + struct stm32_irq error; +}; + +enum i2c_adapter_state { + I2C_STATE_FAULTED = 0, /* Must be zero so undefined transitions land here */ + + I2C_STATE_STOPPED, + I2C_STATE_STOPPING, + I2C_STATE_STARTING, + + I2C_STATE_R_MORE_TXN_ADDR, + I2C_STATE_R_MORE_TXN_PRE_ONE, + I2C_STATE_R_MORE_TXN_PRE_FIRST, + I2C_STATE_R_MORE_TXN_PRE_MIDDLE, + I2C_STATE_R_MORE_TXN_PRE_LAST, + I2C_STATE_R_MORE_TXN_POST_LAST, + + I2C_STATE_R_LAST_TXN_ADDR, + I2C_STATE_R_LAST_TXN_PRE_ONE, + I2C_STATE_R_LAST_TXN_PRE_FIRST, + I2C_STATE_R_LAST_TXN_PRE_MIDDLE, + I2C_STATE_R_LAST_TXN_PRE_LAST, + I2C_STATE_R_LAST_TXN_POST_LAST, + + I2C_STATE_W_MORE_TXN_ADDR, + I2C_STATE_W_MORE_TXN_MIDDLE, + I2C_STATE_W_MORE_TXN_LAST, + + I2C_STATE_W_LAST_TXN_ADDR, + I2C_STATE_W_LAST_TXN_MIDDLE, + I2C_STATE_W_LAST_TXN_LAST, + + I2C_STATE_NUM_STATES /* Must be last */ +}; + +struct pios_i2c_adapter { + const struct pios_i2c_adapter_cfg * const cfg; + void (*callback)(uint8_t, uint8_t); +#ifdef PIOS_INCLUDE_FREERTOS + xSemaphoreHandle sem_busy; + xSemaphoreHandle sem_ready; +#endif + + enum i2c_adapter_state curr_state; + const struct pios_i2c_txn * first_txn; + const struct pios_i2c_txn * active_txn; + const struct pios_i2c_txn * last_txn; + + uint8_t * active_byte; + uint8_t * last_byte; +}; + +extern struct pios_i2c_adapter pios_i2c_adapters[]; +extern uint8_t pios_i2c_num_adapters; + +#endif /* PIOS_I2C_PRIV_H */