Skip to content

Instantly share code, notes, and snippets.

@shengwen-tw
Created August 24, 2022 16:34
Show Gist options
  • Save shengwen-tw/a597f1703519c4b3a954c831794463ac to your computer and use it in GitHub Desktop.
Save shengwen-tw/a597f1703519c4b3a954c831794463ac to your computer and use it in GitHub Desktop.
#include <stm32f4xx.h>
#include "sys_time.h"
#include "sys_time.h"
#include "i2c.h"
#define I2C_TIMEOUT_MS 50
enum {
I2C_READ_MODE,
I2C_WRITE_MODE
} I2C_Mode;
enum {
I2C_IDLE,
I2C_SEND_START,
I2C_SEND_REG_ADDR,
I2C_SEND_START_AGAIN1,
I2C_SEND_START_AGAIN2,
I2C_READY_TO_READ,
I2C_READ_1_BYTE,
I2C_READ_2_BYTES,
I2C_READ_N_BYTES,
I2C_WRITE_BYTE,
I2C_WAIT_ACK,
} I2C_StateMachine;
typedef struct {
uint8_t state;
uint8_t mode;
uint8_t dev_addr;
uint8_t reg_addr;
uint8_t send_data;
uint8_t *rx_buffer;
size_t rx_index;
size_t size;
} i2c_driver_t;
SemaphoreHandle_t i2c2_semphr;
i2c_driver_t i2c2;
void i2c2_init(void)
{
i2c2_semphr = xSemaphoreCreateBinary();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* pb10 = scl,pb11 = sda */
GPIO_InitTypeDef GPIO_InitStructure= {
.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11,
.GPIO_Speed = GPIO_Speed_100MHz,
.GPIO_Mode = GPIO_Mode_AF,
.GPIO_OType = GPIO_OType_OD,
.GPIO_PuPd = GPIO_PuPd_UP //GPIO_PuPd_NOPULL
};
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2);
I2C_InitTypeDef I2C_InitStructure = {
.I2C_Mode = I2C_Mode_I2C,
.I2C_ClockSpeed = 100000, //100kHz
.I2C_DutyCycle = I2C_DutyCycle_2,
.I2C_OwnAddress1 = 0,
.I2C_Ack = I2C_Ack_Enable,
.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit
};
I2C_Init(I2C2, &I2C_InitStructure);
NVIC_InitTypeDef NVIC_InitStruct = {
.NVIC_IRQChannel = I2C2_EV_IRQn,
.NVIC_IRQChannelPreemptionPriority = I2C_DEV_PRIORITY,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE
};
NVIC_Init(&NVIC_InitStruct);
/* enable i2c */
I2C_Cmd(I2C2,ENABLE);
}
int i2c_blocked_start_tx_mode(I2C_TypeDef* i2c, uint8_t address)
{
/* wait until i2c is not busy anymore */
float start_time = get_sys_time_ms();
while(I2C_GetFlagStatus(i2c, I2C_FLAG_BUSY)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
/* send i2c start condition */
I2C_GenerateSTART(i2c, ENABLE);
/* wait until slave acknowledged the start condition */
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_MODE_SELECT)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
/* send slave address */
I2C_Send7bitAddress(i2c, (address << 1) | 0, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
return 0;
}
int i2c_blocked_start_rx_mode(I2C_TypeDef* i2c, uint8_t address)
{
/* wait until i2c is not busy anymore */
float start_time = get_sys_time_ms();
while(I2C_GetFlagStatus(i2c, I2C_FLAG_BUSY)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
/* send i2c start condition */
I2C_GenerateSTART(i2c, ENABLE);
/* wait until slave acknowledged the start condition */
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_MODE_SELECT)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
/* send slave address */
I2C_Send7bitAddress(i2c, (address << 1) | 1, I2C_Direction_Receiver);
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
return 0;
}
int i2c_blocked_write(I2C_TypeDef* i2c, uint8_t data)
{
float start_time = get_sys_time_ms();
/* send one byte */
I2C_SendData(i2c, data);
/* wait until i2c byte transmission is complete */
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
return 0;
}
uint8_t i2c_blocked_read_ack(I2C_TypeDef* i2c)
{
float start_time = get_sys_time_ms();
uint8_t data;
/* enable acknowledgement */
I2C_AcknowledgeConfig(i2c, ENABLE);
/* wait until the received flag is set */
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_BYTE_RECEIVED)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
/* receive a byte */
data = I2C_ReceiveData(i2c);
return data;
}
uint8_t i2c_blocked_read_nack(I2C_TypeDef* i2c)
{
float start_time = get_sys_time_ms();
uint8_t data;
/* disable the acknowledgement */
I2C_AcknowledgeConfig(i2c, DISABLE);
/* wait until the received flag is set */
while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_BYTE_RECEIVED)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
/* receive one byte */
data = I2C_ReceiveData(i2c);
return data;
}
void i2c_blocked_stop(I2C_TypeDef* i2c)
{
/* generate stop signal */
I2C_GenerateSTOP(i2c, ENABLE);
}
uint8_t i2c2_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, size_t size)
{
/* wait until i2c is not busy anymore */
float start_time = get_sys_time_ms();
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
i2c2.mode = I2C_READ_MODE;
i2c2.dev_addr = dev_addr;
i2c2.reg_addr = reg_addr;
i2c2.rx_buffer = data;
i2c2.rx_index = 0;
i2c2.size = size;
i2c2.state = I2C_SEND_START;
I2C_AcknowledgeConfig(I2C2, ENABLE);
I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
/* send i2c start condition */
I2C_GenerateSTART(I2C2, ENABLE);
xSemaphoreTake(i2c2_semphr, portMAX_DELAY);
I2C_ITConfig(I2C2, I2C_IT_EVT, DISABLE);
return 0;
}
uint8_t i2c2_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
/* wait until i2c is not busy anymore */
float start_time = get_sys_time_ms();
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)) {
if((get_sys_time_ms() - start_time) > I2C_TIMEOUT_MS) {
return 1;
}
};
i2c2.mode = I2C_WRITE_MODE;
i2c2.dev_addr = dev_addr;
i2c2.reg_addr = reg_addr;
i2c2.send_data = data;
i2c2.state = I2C_SEND_START;
I2C_AcknowledgeConfig(I2C2, ENABLE);
I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
/* send i2c start condition */
I2C_GenerateSTART(I2C2, ENABLE);
xSemaphoreTake(i2c2_semphr, portMAX_DELAY);
I2C_ITConfig(I2C2, I2C_IT_EVT, DISABLE);
return 0;
}
void i2c2_read_handler(void)
{
BaseType_t higher_priority_task_woken = pdFALSE;
switch(i2c2.state) {
case I2C_SEND_START: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)) {
/* send slave address */
I2C_Send7bitAddress(I2C2, (i2c2.dev_addr << 1) | 0, I2C_Direction_Transmitter);
i2c2.state = I2C_SEND_REG_ADDR;
}
break;
}
case I2C_SEND_REG_ADDR: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
/* send one byte address */
I2C_SendData(I2C2, i2c2.reg_addr);
i2c2.state = I2C_SEND_START_AGAIN1;
}
break;
}
case I2C_SEND_START_AGAIN1: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
/* generate stop signal */
I2C_GenerateSTOP(I2C2, ENABLE);
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));
/* generate start signal */
I2C_GenerateSTART(I2C2, ENABLE);
i2c2.state = I2C_SEND_START_AGAIN2;
}
break;
}
case I2C_SEND_START_AGAIN2: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)) {
/* send slave address */
I2C_Send7bitAddress(I2C2, (i2c2.dev_addr << 1) | 1, I2C_Direction_Receiver);
if(i2c2.size == 2) {
i2c2.state = I2C_READ_2_BYTES;
} else {
i2c2.state = I2C_READ_1_BYTE;
}
}
break;
}
case I2C_READ_1_BYTE: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {
if(i2c2.size == 1) {
I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);
}
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_RXNE) != SET);
i2c2.rx_buffer[i2c2.rx_index] = I2C_ReceiveData(I2C2);
i2c2.rx_index++;
if(i2c2.size == 1) {
i2c2.state = I2C_IDLE;
I2C_ITConfig(I2C2, I2C_IT_EVT, DISABLE);
xSemaphoreGiveFromISR(i2c2_semphr, &higher_priority_task_woken);
} else {
i2c2.state = I2C_READ_N_BYTES;
}
}
break;
}
case I2C_READ_2_BYTES: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {
/* set ack=0 and pos=1 */
I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_NACKPositionConfig(I2C2, I2C_NACKPosition_Next);
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BTF) != SET);
I2C_GenerateSTOP(I2C2, ENABLE);
i2c2.rx_buffer[0] = I2C_ReceiveData(I2C2);
i2c2.rx_buffer[1] = I2C_ReceiveData(I2C2);
i2c2.state = I2C_IDLE;
I2C_ITConfig(I2C2, I2C_IT_EVT, DISABLE);
xSemaphoreGiveFromISR(i2c2_semphr, &higher_priority_task_woken);
}
break;
}
case I2C_READ_N_BYTES: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)) {
if(i2c2.rx_index == (i2c2.size - 2)) {
I2C_AcknowledgeConfig(I2C2, DISABLE);
}
if(i2c2.rx_index == (i2c2.size - 1)) {
I2C_GenerateSTOP(I2C2, ENABLE);
}
/* receive a byte */
i2c2.rx_buffer[i2c2.rx_index] = I2C_ReceiveData(I2C2);
i2c2.rx_index++;
if(i2c2.rx_index == i2c2.size) {
/* received all bytes, ready to leave */
i2c2.state = I2C_IDLE;
I2C_ITConfig(I2C2, I2C_IT_EVT, DISABLE);
xSemaphoreGiveFromISR(i2c2_semphr, &higher_priority_task_woken);
}
}
break;
}
}
portEND_SWITCHING_ISR(higher_priority_task_woken);
}
void i2c2_write_handler(void)
{
BaseType_t higher_priority_task_woken = pdFALSE;
switch(i2c2.state) {
case I2C_SEND_START: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)) {
/* send slave address */
I2C_Send7bitAddress(I2C2, (i2c2.dev_addr << 1) | 0, I2C_Direction_Transmitter);
i2c2.state = I2C_SEND_REG_ADDR;
}
break;
}
case I2C_SEND_REG_ADDR: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
/* send one byte address */
I2C_SendData(I2C2, i2c2.reg_addr);
i2c2.state = I2C_WRITE_BYTE;
}
break;
}
case I2C_WRITE_BYTE: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
/* send one byte data */
I2C_SendData(I2C2, i2c2.send_data);
i2c2.state = I2C_WAIT_ACK;
}
break;
}
case I2C_WAIT_ACK: {
if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
/* generate stop signal */
I2C_GenerateSTOP(I2C2, ENABLE);
i2c2.state = I2C_IDLE;
I2C_ITConfig(I2C2, I2C_IT_EVT, DISABLE);
xSemaphoreGiveFromISR(i2c2_semphr, &higher_priority_task_woken);
}
}
}
portEND_SWITCHING_ISR(higher_priority_task_woken);
}
void I2C2_EV_IRQHandler(void)
{
if(i2c2.mode == I2C_READ_MODE) {
i2c2_read_handler();
} else if(i2c2.mode == I2C_WRITE_MODE) {
i2c2_write_handler();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment