概述:i2c通讯接口,是我们日常应用中使用最多的mcu外设。最早在mcu没有硬件i2c之前,都是通过gpio口模拟i2c的时序来完成i2c通讯的。后来mcu带有了i2c外设接口,其硬件i2c的使用也变成了日常。更主要的是,在节省mcu资源的同时,其操作也变得更加简单和易用。再后来经过市场需求的变化,开始有了支持i2c多从机地址通讯功能的mcu,让i2c的应用紧跟市场需求。 虽然从i2c特性上知晓具有不同i2c地址的器件是可以挂载在同一个i2c总线上进行通讯的,但是,如果需要操作的i2c器件地址冲突呢?mcu的硬件i2c接口数量不够呢?或者说mcu的i2c不支持从机多地址通讯功能呢?这时候,我们还是需要通过gpio口来模拟i2c时序完成i2c主机/从机的功能。所以,并不是有了硬件i2c,软件i2c就没有发挥的空间了,恰恰是软件和硬件这两种实现方式共存互相补充。
对于i2c的基本概念及时序等知识点,本文不再详细描述,大家可以下载附件中的《i2c总线概要》和《i2c总线规范》进行研究。本文将通过如下四个方面,讲述i2c在mm32f032/mm32f0140系列mcu上的实现,以及使用i2c工具(图莫斯usb2xxx总线适配器)进行实际测试: 硬件i2c主机通讯 软件模拟i2c主机通讯 硬件i2c从机通讯 软件模拟i2c从机通讯(有难度) mm32f032系列mcu带有1路硬件i2c接口,支持标准模式(数据传输速率为0~100kbps)和快速模式(数据最大传输速率为400kbps)两种工作速率模式,其主要特征如下所示: i2c总线协议转换器/并行总线; 半双工同步操作; 支持主从模式; 支持7位地址和10位地址; 支持标准模式100kbps、快速模式400kbps; 产生start、stop、repeated start,以及acknowledge信号检测; 在主机模式下只支持一个主机; 分别有2个字节的发送和接收缓冲; 在scl和sda上增加了无毛刺电路; 支持dam、中断和查询操作方式; mm32f0140系列mcu在mm32f032的基础上i2c做了更丰富的功能,支持多从机地址通讯的功能、支持时钟延展等等……具体的可以参考官方的数据手册。
一、硬件i2c主机通讯
mm32的硬件i2c是我使用到现在,在代码程序段操作最为简洁的了;不需要再去考虑start信号、ack信号,以及各种event事件等……这些复杂的操作、或者是可以省略的操作都由官方的底层库程序和芯片ip去实现了,让我们在设计驱动程序时变量简单了。对于硬件i2c主机的配置,我们只需要复用的gpio端口引脚、i2c通讯参数,以及从机地址即可;然后就可以编程去读写i2c从机设备了,初始化配置及对i2c从机设备的读写操作的实现代码如下:
void hi2c_master_init(uint8_t slaveaddress){ gpio_inittypedef gpio_initstructure; i2c_inittypedef i2c_initstructure; rcc_apb1periphclockcmd(rcc_apb1enr_i2c1, enable); i2c_structinit(&i2c_initstructure); i2c_initstructure.i2c_mode = i2c_mode_master; i2c_initstructure.i2c_ownaddress = 0; i2c_initstructure.i2c_speed = i2c_speed_standard; i2c_initstructure.i2c_clockspeed = 100000; i2c_init(i2c1, &i2c_initstructure); i2c_send7bitaddress(i2c1, slaveaddress, i2c_direction_transmitter); i2c_cmd(i2c1, enable); rcc_ahbperiphclockcmd(rcc_ahbenr_gpiob, enable); gpio_pinafconfig(gpiob, gpio_pinsource6, gpio_af_1); gpio_pinafconfig(gpiob, gpio_pinsource7, gpio_af_1); gpio_structinit(&gpio_initstructure); gpio_initstructure.gpio_pin = gpio_pin_6 | gpio_pin_7; gpio_initstructure.gpio_speed = gpio_speed_2mhz; gpio_initstructure.gpio_mode = gpio_mode_af_od; gpio_init(gpiob, &gpio_initstructure);}void hi2c_master_read(uint8_t address, uint8_t *buffer, uint8_t length){ uint8_t flag = 0, count = 0; i2c_senddata(i2c1, address); while(!i2c_getflagstatus(i2c1, i2c_status_flag_tfe)); for(uint8_t i = 0; i < length; i++) { while(1) { if((i2c_getflagstatus(i2c1, i2c_status_flag_tfnf)) && (flag == 0)) { i2c_readcmd(i2c1); count++; if(count == length) flag = 1; } if(i2c_getflagstatus(i2c1, i2c_status_flag_rfne)) { buffer[i] = i2c_receivedata(i2c1); break; } } } i2c_generatestop(i2c1, enable); while(!i2c_getflagstatus(i2c1, i2c_flag_stop_det));}void hi2c_master_write(uint8_t address, uint8_t *buffer, uint8_t length){ i2c_senddata(i2c1, address); while(!i2c_getflagstatus(i2c1, i2c_status_flag_tfe)); for(uint8_t i = 0; i < length; i++) { i2c_senddata(i2c1, *buffer++); while(!i2c_getflagstatus(i2c1, i2c_status_flag_tfe)); } i2c_generatestop(i2c1, enable); while(!i2c_getflagstatus(i2c1, i2c_flag_stop_det));}void hi2c_master_shell_handler(uint8_t mode){ uint8_t buffer[10] = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0xaa}; if(mode == 1) { hi2c_master_write(0x00, buffer, sizeof(buffer)); } else { hi2c_master_read(0x00, buffer, sizeof(buffer)); printf(hi2c master read : ); for(uint8_t i = 0; i scl_gpio, si2c->scl_pin, bit_set)#define si2c_master_scl_l(si2c) gpio_writebit(si2c->scl_gpio, si2c->scl_pin, bit_reset)#define si2c_master_sda_h(si2c) gpio_writebit(si2c->sda_gpio, si2c->sda_pin, bit_set)#define si2c_master_sda_l(si2c) gpio_writebit(si2c->sda_gpio, si2c->sda_pin, bit_reset)#define si2c_master_scl_get(si2c) gpio_readoutputdatabit(si2c->scl_gpio, si2c->scl_pin)#define si2c_master_sda_get(si2c) gpio_readinputdatabit( si2c->sda_gpio, si2c->sda_pin)void si2c_master_delay(uint32_t tick){ while(tick--);}void si2c_master_sda_setdirection(si2c_master_typedef *si2c, uint8_t direction){ gpio_inittypedef gpio_initstructure; rcc_ahbperiphclockcmd(si2c->sda_rcc, enable); gpio_structinit(&gpio_initstructure); gpio_initstructure.gpio_pin = si2c->sda_pin; gpio_initstructure.gpio_speed = gpio_speed_50mhz; if(direction) /* input */ { gpio_initstructure.gpio_mode = gpio_mode_in_floating; } else /* output */ { gpio_initstructure.gpio_mode = gpio_mode_out_pp; } gpio_init(si2c->sda_gpio, &gpio_initstructure);}void si2c_master_scl_setdirection(si2c_master_typedef *si2c, uint8_t direction){ gpio_inittypedef gpio_initstructure; rcc_ahbperiphclockcmd(si2c->scl_rcc, enable); gpio_structinit(&gpio_initstructure); gpio_initstructure.gpio_pin = si2c->scl_pin; gpio_initstructure.gpio_speed = gpio_speed_50mhz; if(direction) /* input */ { gpio_initstructure.gpio_mode = gpio_mode_in_floating; } else /* output */ { gpio_initstructure.gpio_mode = gpio_mode_out_pp; } gpio_init(si2c->scl_gpio, &gpio_initstructure);}void si2c_master_generatestart(si2c_master_typedef *si2c){ si2c_master_sda_h(si2c); si2c_master_delay(si2c->time); si2c_master_scl_h(si2c); si2c_master_delay(si2c->time); si2c_master_sda_l(si2c); si2c_master_delay(si2c->time); si2c_master_scl_l(si2c); si2c_master_delay(si2c->time);}void si2c_master_generatestop(si2c_master_typedef *si2c){ si2c_master_sda_l(si2c); si2c_master_delay(si2c->time); si2c_master_scl_h(si2c); si2c_master_delay(si2c->time); si2c_master_sda_h(si2c); si2c_master_delay(si2c->time);}void si2c_master_putack(si2c_master_typedef *si2c, uint8_t ack){ if(ack) si2c_master_sda_h(si2c); /* nack */ else si2c_master_sda_l(si2c); /* ack */ si2c_master_delay(si2c->time); si2c_master_scl_h(si2c); si2c_master_delay(si2c->time); si2c_master_scl_l(si2c); si2c_master_delay(si2c->time);}uint8_t si2c_master_getack(si2c_master_typedef *si2c){ uint8_t ack = 0; si2c_master_sda_h(si2c); si2c_master_delay(si2c->time); si2c_master_sda_setdirection(si2c, 1); si2c_master_scl_h(si2c); si2c_master_delay(si2c->time); ack = si2c_master_sda_get(si2c); si2c_master_scl_l(si2c); si2c_master_delay(si2c->time); si2c_master_sda_setdirection(si2c, 0); return ack;}uint8_t si2c_master_readbyte(si2c_master_typedef *si2c){ uint8_t data = 0; si2c_master_sda_h(si2c); /* must set sda before read */ si2c_master_sda_setdirection(si2c, 1); for(uint8_t i = 0; i time); data