单片机IIC实验
单片机 STM32F407开发板DMF407电机开发板平台keil V5.31HSE 为8MHZHSI为16MHZ原理图主函数int main(void) { uint8_t key; uint16_t i 0; uint8_t datatemp[TEXT_SIZE]; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */ delay_init(168); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ usmart_dev.init(84); /* 初始化USMART */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ key_init(); /* 初始化按键 */ at24cxx_init(); /* 初始化24CXX */ lcd_show_string(30, 50, 200, 16, 16, STM32, RED); lcd_show_string(30, 70, 200, 16, 16, IIC TEST, RED); lcd_show_string(30, 90, 200, 16, 16, ATOMALIENTEK, RED); lcd_show_string(30, 110, 200, 16, 16, KEY1:Write KEY0:Read, RED); /* 显示提示信息 */ while (at24cxx_check()) /* 检测不到24C02 */ { lcd_show_string(30, 130, 200, 16, 16, 24C02 Check Failed!, RED); delay_ms(500); lcd_show_string(30, 130, 200, 16, 16, Please Check! , RED); delay_ms(500); LED0_TOGGLE(); /* 红灯闪烁 */ } lcd_show_string(30, 130, 200, 16, 16, 24C02 Ready!, RED); while (1) { key key_scan(0); if (key KEY1_PRES) /* KEY1按下,写入24C02 */ { lcd_fill(0, 150, 239, 319, WHITE); /* 清除半屏 */ lcd_show_string(30, 150, 200, 16, 16, Start Write 24C02...., BLUE); at24cxx_write(0, (uint8_t *)g_text_buf, TEXT_SIZE); lcd_show_string(30, 150, 200, 16, 16, 24C02 Write Finished!, BLUE); /* 提示传送完成 */ } if (key KEY0_PRES) /* KEY0按下,读取字符串并显示 */ { lcd_show_string(30, 150, 200, 16, 16, Start Read 24C02.... , BLUE); at24cxx_read(0, datatemp, TEXT_SIZE); lcd_show_string(30, 150, 200, 16, 16, The Data Readed Is: , BLUE); /* 提示传送完成 */ lcd_show_string(30, 170, 200, 16, 16, (char *)datatemp, BLUE); /* 显示读到的字符串 */ } i; if (i 20) { LED0_TOGGLE(); /* 红灯闪烁 */ i 0; } delay_ms(10); } }配置/* 引脚 定义 */ #define IIC_SCL_GPIO_PORT GPIOH #define IIC_SCL_GPIO_PIN GPIO_PIN_4 #define IIC_SCL_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PB口时钟使能 */ #define IIC_SDA_GPIO_PORT GPIOH #define IIC_SDA_GPIO_PIN GPIO_PIN_5 #define IIC_SDA_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PB口时钟使能 */ /******************************************************************************************/ /* IO操作 */ #define IIC_SCL(x) do{ x ? \ HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_RESET); \ }while(0) /* SCL */ #define IIC_SDA(x) do{ x ? \ HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_RESET); \ }while(0) /* SDA */ #define IIC_READ_SDA HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) /* 读取SDA */ void iic_init(void) { GPIO_InitTypeDef gpio_init_struct; IIC_SCL_GPIO_CLK_ENABLE(); /* SCL引脚时钟使能 */ IIC_SDA_GPIO_CLK_ENABLE(); /* SDA引脚时钟使能 */ gpio_init_struct.Pin IIC_SCL_GPIO_PIN; gpio_init_struct.Mode GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ gpio_init_struct.Pull GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed GPIO_SPEED_FREQ_VERY_HIGH; /* 快速 */ HAL_GPIO_Init(IIC_SCL_GPIO_PORT, gpio_init_struct);/* SCL */ gpio_init_struct.Pin IIC_SDA_GPIO_PIN; gpio_init_struct.Mode GPIO_MODE_OUTPUT_OD; /* 开漏输出 */ HAL_GPIO_Init(IIC_SDA_GPIO_PORT, gpio_init_struct);/* SDA */ /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(1), 也可以读取外部信号的高低电平 */ iic_stop(); /* 停止总线上所有设备 */ } uint8_t at24cxx_read_one_byte(uint16_t addr) { uint8_t temp 0; iic_start(); /* 发送起始信号 */ /* 根据不同的24CXX型号, 发送高位地址 * 1, 24C16以上的型号, 分2个字节发送地址 * 2, 24C16及以下的型号, 分1个低字节地址 占用器件地址的bit1~bit3位 用于表示高位地址, 最多11位地址 * 对于24C01/02, 其器件地址格式(8bit)为: 1 0 1 0 A2 A1 A0 R/W * 对于24C04, 其器件地址格式(8bit)为: 1 0 1 0 A2 A1 a8 R/W * 对于24C08, 其器件地址格式(8bit)为: 1 0 1 0 A2 a9 a8 R/W * 对于24C16, 其器件地址格式(8bit)为: 1 0 1 0 a10 a9 a8 R/W * R/W : 读/写控制位 0,表示写; 1,表示读; * A0/A1/A2 : 对应器件的1,2,3引脚(只有24C01/02/04/8有这些脚) * a8/a9/a10: 对应存储整列的高位地址, 11bit地址最多可以表示2048个位置, 可以寻址24C16及以内的型号 */ if (EE_TYPE AT24C16) /* 24C16以上的型号, 分2个字节发送地址 */ { iic_send_byte(0xA0); /* 发送写命令, IIC规定最低位是0, 表示写入 */ iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr 8); /* 发送高字节地址 */ } else { iic_send_byte(0xA0 ((addr 8) 1)); /* 发送器件 0xA0 高位a8/a9/a10地址,写数据 */ } iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr % 256); /* 发送低位地址 */ iic_wait_ack(); /* 等待ACK, 此时地址发送完成了 */ iic_start(); /* 重新发送起始信号 */ iic_send_byte(0xA1); /* 进入接收模式, IIC规定最低位是1, 表示读取 */ iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ temp iic_read_byte(0); /* 接收一个字节数据 */ iic_stop(); /* 产生一个停止条件 */ return temp; } /** * brief 在AT24CXX指定地址写入一个数据 * param addr: 写入数据的目的地址 * param data: 要写入的数据 * retval 无 */ void at24cxx_write_one_byte(uint16_t addr, uint8_t data) { /* 原理说明见:at24cxx_read_one_byte函数, 本函数完全类似 */ iic_start(); /* 发送起始信号 */ if (EE_TYPE AT24C16) /* 24C16以上的型号, 分2个字节发送地址 */ { iic_send_byte(0xA0); /* 发送写命令, IIC规定最低位是0, 表示写入 */ iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr 8); /* 发送高字节地址 */ } else { iic_send_byte(0xA0 ((addr 8) 1)); /* 发送器件 0xA0 高位a8/a9/a10地址,写数据 */ } iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr % 256); /* 发送低位地址 */ iic_wait_ack(); /* 等待ACK, 此时地址发送完成了 */ /* 因为写数据的时候,不需要进入接收模式了,所以这里不用重新发送起始信号了 */ iic_send_byte(data); /* 发送1字节 */ iic_wait_ack(); /* 等待ACK */ iic_stop(); /* 产生一个停止条件 */ delay_ms(10); /* 注意: EEPROM 写入比较慢,必须等到10ms后再写下一个字节 */ } /** * brief 检查AT24CXX是否正常 * note 检测原理: 在器件的末地址写如0X55, 然后再读取, 如果读取值为0X55 * 则表示检测正常. 否则,则表示检测失败. * * param 无 * retval 检测结果 * 0: 检测成功 * 1: 检测失败 */ uint8_t at24cxx_check(void) { uint8_t temp; uint16_t addr EE_TYPE; temp at24cxx_read_one_byte(addr); /* 避免每次开机都写AT24CXX */ if (temp 0x55) /* 读取数据正常 */ { return 0; } else /* 排除第一次初始化的情况 */ { at24cxx_write_one_byte(addr, 0x55); /* 先写入数据 */ temp at24cxx_read_one_byte(255); /* 再读取数据 */ if (temp 0x55)return 0; } return 1; } /** * brief 在AT24CXX里面的指定地址开始读出指定个数的数据 * param addr : 开始读出的地址 对24c02为0~255 * param pbuf : 数据数组首地址 * param datalen : 要读出数据的个数 * retval 无 */ void at24cxx_read(uint16_t addr, uint8_t *pbuf, uint16_t datalen) { while (datalen--) { *pbuf at24cxx_read_one_byte(addr); } } /** * brief 在AT24CXX里面的指定地址开始写入指定个数的数据 * param addr : 开始写入的地址 对24c02为0~255 * param pbuf : 数据数组首地址 * param datalen : 要写入数据的个数 * retval 无 */ void at24cxx_write(uint16_t addr, uint8_t *pbuf, uint16_t datalen) { while (datalen--) { at24cxx_write_one_byte(addr, *pbuf); addr; pbuf; } }实验结果