Created
August 24, 2022 16:34
-
-
Save shengwen-tw/a597f1703519c4b3a954c831794463ac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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