/************************************************************************************************/ /** * @file i2c_bsp.c * @author MCU Ecosystem Development Team + 我 * @brief I2C BSP驱动函数,使用GPIO模拟I2C主模式,实现I2C主模式通信协议。 * * ************************************************************************************************** * @attention * Copyright (c) CEC Huada Electronic Design Co.,Ltd. All rights reserved. * ************************************************************************************************** */ #include "i2c_bsp.h" /* 华大i2c_bsp.c 模块说明 注意:本模块为了更适合使用,已经进行了修改了,标准100k iic通信 注意:延时函数一定要保证1us为基本单位: i2c_delay_time(uint32_t delay_time) 这延时必须要实现1us 在不同的系统频率下 标准100k,iic通信时序要求; 1.数据保存时间: 5us及以上 2.数据建立时间: 5us及以上 3.低电平时间: 5us及以上 4.高电平时间: 5us及以上 注意:模拟的一般只适合标准iic 100k的通信速率. 要根据系统时钟的修改,验证i2c_delay_time(1)必须是1us 使用方法: uint8_t err_flag = 0; uint8_t BULL[8] = {0X00, OX11, 0X22 , 0X33 , 0X44, 0X55 , 0X66 , 0X77}; void my_iic_err_call(void){ err_flag = 1; } int main(void){ SN_SYSCLK_set(SYSCLK_48MHZ); std_delay_init(); SN_IIC_IO_set(IIC_SCL_PB3 ,IIC_SCL_PB4 ,IIC_PULLUP); //设置iic通信引脚 SN_IIC_ERROR_call(my_iic_err_call); //设置错误处理函数 SN_IIC_slave(0x5a); //设置通信从机地址 while(1){ std_delayms(500); bsp_i2c_master_send(BULL,8); //发送数据给从机0x5a std_delayms(500); bsp_i2c_master_receive(BULL,8); //从0x5a从机中接收数据 } } */ __IO uint8_t SLAVE_ADDRESS = 0x00 ; //全局变量,从机id号码 GPIO_t * I2C_PIN_PORT1 = NULL; //SCL的端口 GPIO_t * I2C_PIN_PORT2 = NULL; //SDA的端口 __IO uint16_t SCL_PIN = 0x0000000; //SCL引脚 __IO uint16_t SDA_PIN = 0x0000000; //SDA引脚 void (* SN_IIC_error_call ) (void) = NULL; //iic通信错误处理函数指针 /* 函数: SN_IIC_IO_set(uint8_t SCL_PIN_x ,uint8_t SDA_PIN_x) 功能: 设置i2c_bsp模块的io引脚使用 参数: SCL_PIN_x @IIC_SCL_PA0 @IIC_SCL_PA1 @IIC_SCL_PA2 (仿真线) @IIC_SCL_PA3 @IIC_SCL_PA4 @IIC_SCL_PA5 @IIC_SCL_PA6 @IIC_SCL_PA7 @IIC_SCL_PB0 @IIC_SCL_PB1 @IIC_SCL_PB2 @IIC_SCL_PB3 @IIC_SCL_PB4 @IIC_SCL_PB5 @IIC_SCL_PB6 (仿真线) @IIC_SCL_PB7 @IIC_SCL_PC0 (复位线,必须修改选项字节才可以使用) @IIC_SCL_PC1 参数: SDA_PIN_x @IIC_SDA_PA0 @IIC_SDA_PA1 @IIC_SDA_PA2 (仿真线) @IIC_SDA_PA3 @IIC_SDA_PA4 @IIC_SDA_PA5 @IIC_SDA_PA6 @IIC_SDA_PA7 @IIC_SDA_PB0 @IIC_SDA_PB1 @IIC_SDA_PB2 @IIC_SDA_PB3 @IIC_SDA_PB4 @IIC_SDA_PB5 @IIC_SDA_PB6 (仿真线) @IIC_SDA_PB7 @IIC_SDA_PC0 (复位线,必须修改选项字节才可以使用) @IIC_SDA_PC1 参数: 是否使用内部上拉 @IIC_PULLUP_NULL //不使用内部上拉,用户自己在iic总线加上拉电阻 @IIC_PULLUP //使用内部30k上拉电阻 100k标准协议不影响 返回:无 */ void SN_IIC_IO_set(uint8_t SCL_PIN_x ,uint8_t SDA_PIN_x ,uint8_t PULLUP ) { uint32_t GPIO_PULLUP_x = 0; GPIO_t * GPIO_x = NULL; uint16_t GPIO_PIN_x = 0; uint32_t RCC_PERIPH_CLK_GPIO_x = 0; std_gpio_init_t gpio_config = {0}; //是否使用内部上拉电阻 GPIO_PULLUP_x = (PULLUP) ? GPIO_PULLUP : GPIO_NOPULL ; //处理SCL GPIO_x = (SCL_PIN_x < 9)? GPIOA : (SCL_PIN_x < 16) ? GPIOB : GPIOC ; RCC_PERIPH_CLK_GPIO_x = (SCL_PIN_x < 9)? RCC_PERIPH_CLK_GPIOA : (SCL_PIN_x < 16) ? RCC_PERIPH_CLK_GPIOB : RCC_PERIPH_CLK_GPIOC ; GPIO_PIN_x = (SCL_PIN_x < 9) ? (GPIO_PIN_0 << (SCL_PIN_x - 1)) : (SCL_PIN_x < 16) ? ( GPIO_PIN_0 << (SCL_PIN_x - 9)) : ( GPIO_PIN_0 << (SCL_PIN_x - 17)) ; //设置全局 scl引脚 I2C_PIN_PORT1 = GPIO_x ; SCL_PIN = GPIO_PIN_x ; std_rcc_gpio_clk_enable(RCC_PERIPH_CLK_GPIO_x); gpio_config.pin = GPIO_PIN_x; gpio_config.mode = GPIO_MODE_OUTPUT; gpio_config.output_type = GPIO_OUTPUT_OPENDRAIN; gpio_config.pull = GPIO_PULLUP_x ; std_gpio_init(GPIO_x, &gpio_config); //处理SDA GPIO_x = (SDA_PIN_x < 9)? GPIOA : (SDA_PIN_x < 16) ? GPIOB : GPIOC ; RCC_PERIPH_CLK_GPIO_x = (SDA_PIN_x < 9)? RCC_PERIPH_CLK_GPIOA : (SDA_PIN_x < 16) ? RCC_PERIPH_CLK_GPIOB : RCC_PERIPH_CLK_GPIOC ; GPIO_PIN_x = (SDA_PIN_x < 9) ? (GPIO_PIN_0 << (SDA_PIN_x - 1)) : (SDA_PIN_x < 16) ? ( GPIO_PIN_0 << (SDA_PIN_x - 9)) : ( GPIO_PIN_0 << (SDA_PIN_x - 17)) ; //设置全局 scl引脚 I2C_PIN_PORT2 = GPIO_x ; SDA_PIN = GPIO_PIN_x ; std_rcc_gpio_clk_enable(RCC_PERIPH_CLK_GPIO_x); gpio_config.pin = GPIO_PIN_x; gpio_config.mode = GPIO_MODE_OUTPUT; gpio_config.output_type = GPIO_OUTPUT_OPENDRAIN; std_gpio_init(GPIO_x, &gpio_config); } /* 函数: SN_IIC_slave(uint16_t iic_slave_id ) 功能: 设置通信从机ic 参数: 7位 或 10位 从机id 返回; 无 */ void SN_IIC_slave(uint8_t iic_slave_id ){ SLAVE_ADDRESS = iic_slave_id ; } /* 函数: SN_IIC_ERROR_call(void (*error_call) (void) ) 功能: 设置通信错误处理函数 参数: 错误回调函数 用户自己处理的情况: 1.发出地址没有应答 2,接收数据没有应答 3.超时没有应答 返回; 无 */ void SN_IIC_ERROR_call(void (*error_call) (void)){ SN_IIC_error_call = error_call ; } /** * @brief 异常处理 * @retval 无 */ void error_process(void) { /* 用户添加异常处理流程 */ SN_IIC_error_call(); } /** * @brief I2C延时函数 * @param delay_time 延时计数值 * @note 用户可更改延时计数值来调整I2C的通信速率 注意:软件就延时必须要看系统频率,测试一下这个时间在当前系统频率下是否正确 * @retval 无 注意:必须实现1us的基本单位 */ void i2c_delay_time(uint32_t delay_time) { //------------------------------------软件延时处理-------- uint32_t delay_cnt; for(delay_cnt=delay_time; delay_cnt>0; delay_cnt--); } /** * @brief I2C生成起始位 * @retval 无 */ static void bsp_i2c_generate_start(void) { /* 切换输出模式前固定电平状态以避免毛刺 */ std_gpio_set_pin(I2C_PIN_PORT1, SCL_PIN ); std_gpio_set_pin(I2C_PIN_PORT2, SDA_PIN ); /* 设置SDA为输出模式 */ std_gpio_set_pin_mode(I2C_PIN_PORT2, SDA_PIN, GPIO_MODE_OUTPUT); i2c_delay_time(CLK_DELAY); std_gpio_reset_pin(I2C_PIN_PORT2, SDA_PIN); i2c_delay_time(CLK_DELAY); std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN); } /** * @brief I2C生成停止位 * @retval 无 */ static void bsp_i2c_generate_stop(void) { /* 切换输出模式前固定电平状态以避免毛刺 */ std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN ); std_gpio_reset_pin(I2C_PIN_PORT2, SDA_PIN ); /* 设置SDA为输出模式 */ std_gpio_set_pin_mode(I2C_PIN_PORT2, SDA_PIN, GPIO_MODE_OUTPUT); i2c_delay_time(CLK_DELAY); std_gpio_set_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); std_gpio_set_pin(I2C_PIN_PORT2, SDA_PIN); } /** * @brief I2C生成ACK/NACK * @param ack_type ACK类型 * @arg RET_ACK: 主机发送ACK * @arg RET_NACK: 主机发送NACK * @retval 无 */ static void bsp_i2c_generate_ack(uint32_t ack_type) { /* 切换输出模式前根据返回ACK/NACK固定电平状态以避免毛刺 */ if(ack_type == RET_ACK) { std_gpio_reset_pin(I2C_PIN_PORT2, SDA_PIN); } else { std_gpio_set_pin(I2C_PIN_PORT2, SDA_PIN); } /* 设置SDA为输出模式 */ std_gpio_set_pin_mode(I2C_PIN_PORT2, SDA_PIN, GPIO_MODE_OUTPUT); i2c_delay_time(CLK_DELAY); std_gpio_set_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN); } /** * @brief I2C主模式发送一个字节数据 * @param data 发送数据 * @retval uint32_t 返回从机ACK状态 * @arg RET_ACK: 从机返回ACK * @arg RET_NACK: 从机返回NACK */ static uint32_t bsp_i2c1_master_send_byte(uint8_t send_data) { uint32_t bit_cnt = 0; uint32_t ack_type = 0; /* 切换输出模式前固定电平状态以避免毛刺 */ if (send_data & 0x80) { std_gpio_set_pin(I2C_PIN_PORT2, SDA_PIN); } else { std_gpio_reset_pin(I2C_PIN_PORT2, SDA_PIN); } /* 设置SDA为输出模式 */ std_gpio_set_pin_mode(I2C_PIN_PORT2, SDA_PIN, GPIO_MODE_OUTPUT); /* 发送8bit数据 */ for (bit_cnt=0; bit_cnt < 8; bit_cnt++) { if ((send_data << bit_cnt) & 0x80) { std_gpio_set_pin(I2C_PIN_PORT2, SDA_PIN); } else { std_gpio_reset_pin(I2C_PIN_PORT2, SDA_PIN); } i2c_delay_time(CLK_DELAY); std_gpio_set_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN); } /* 判断从机返回ACK */ std_gpio_set_pin_mode(I2C_PIN_PORT2, SDA_PIN, GPIO_MODE_INPUT); i2c_delay_time(CLK_DELAY); std_gpio_set_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(ACK_DELAY); for (bit_cnt=0; bit_cnt < 5; bit_cnt++) { if (std_gpio_get_input_pin(I2C_PIN_PORT2, SDA_PIN) == false) { ack_type = RET_ACK; } else { ack_type = RET_NACK; break; } } std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); return ack_type; } /** * @brief I2C主模式接收一个字节数据 * @retval uint8_t 接收数据 */ static uint8_t bsp_i2c1_master_receive_byte(void) { uint32_t bit_cnt = 0; uint8_t receive_data = 0; /* 设置SDA为输入状态 */ std_gpio_set_pin_mode(I2C_PIN_PORT2, SDA_PIN, GPIO_MODE_INPUT); /* 接收8bit数据 */ for (bit_cnt=0; bit_cnt<8; bit_cnt++) { std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); std_gpio_set_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); receive_data = receive_data << 1; if (std_gpio_get_input_pin(I2C_PIN_PORT2, SDA_PIN) == true) { receive_data += 1; } } std_gpio_reset_pin(I2C_PIN_PORT1, SCL_PIN); i2c_delay_time(CLK_DELAY); return receive_data; } //------------------------------------------------------------------------------------------------------------------------------------------- /** * @brief I2C主模式发送数据流程 * @retval 无 */ void bsp_i2c_master_send(uint8_t * g_tx_buffer , uint16_t BUFF_SIZE) { uint32_t ack_type; uint32_t index; /* 发送start位 */ bsp_i2c_generate_start(); /* 发送从机地址及写请求 */ ack_type = bsp_i2c1_master_send_byte(SLAVE_ADDRESS | REQ_WRITE); if (ack_type != RET_ACK) { error_process(); } /* 地址匹配转数据发送延时 */ /* 用户可根据实际从机地址匹配后的时序进行调整 */ i2c_delay_time(USER_DELAY); /* 发送数据 */ for (index=0; index