好几天没更新了,因为这两天在研究IIC,跟以前一样,也是踩了一些坑。

一、硬件

        我用的开发板上自带了两个M24C02(EEPROM),一个是IIC通讯,一个是SPI通讯。

        黄色标注的P7、P10、P11指的不是新品的P引脚,而是局部电路代号,从开发板原理图中可以找到接线局部图,下面逐个介绍。

1、P11部分

        代表了M24C02的WP引脚电平接线,短路帽的接法决定了WP引脚电平的高低(低电平才可读写)。(下面是M24C02的芯片引脚接线图以及WP引脚的说明)

2、P7和P10

        这俩决定了将IIC/SPI协议的M24C02引脚接到芯片的P03、P04引脚。

        这时再看芯片原理图,发现P03和P04对应了IIC的SDA10和SCL10通道,这样就知道Code Generator时应该怎么选通道了。

二、软件

1、Code Generator之IIC通道

        通过上一节的介绍,知道要想使用开发板上的M24C02,需要接通IIC10通道,所以Serial模块里面,需要将Channel 2通道配置为IIC10功能。

        点击IIC10页,有3个部分需要设置。

        Transfer rate setting,传输速率,这里默认100000比特率,如果从机的速率较快,可以适当增加。

        Interrupt setting,中断设置,这里设置中断优先级。

        Callback function setting,响应函数设置。勾选后,完成任务就会自动进入预设函数,在里面可以写想要运行的命令(置标志位等)。三个任务分别是,主机传输任务结束时、主机接收任务完成时,主机错误时。

2、自动生成的函数

2.1 r_cg_serial.c 

        生成的函数如上图所示。

2.1.1 R_SAU0_Create()

        这个函数自动放在r_systeminit.c里面.

2.1.2  R_IIC10_Create()

        R_IIC10_Create(),这个函数是用来配置IIC10通道的,这个比较特殊,需要放在main函数的R_MIAN_UserInit()里面,因为IIC10是属于单元SAU0、通道channel 2 的一个子功能,这种底层子功能的初始化配置,需要自己手动放在R_MIAN_UserInit()里面使用。

2.1.3 R_IIC10_Master_Send()        

        R_IIC10_Master_Send(),IIC主机发送若干字节,但是要注意里面不包含Stop时序,需要在发送完后自动调入的r_iic10_callback_master_sendend()函数里面手动填入Stop时序,而Stop时序是由R_IIC10_StopCondition()函数生成的。

        刚看到这个函数具体内容会比较疑惑,里面好像只看到了开始时序R_IIC10_StartCondition(),然后就没有别的内容了,它是怎么发送字节的呢?

        通过观察和查手册,发现倒数第二行使能了INTIIC10这个中断,然后SIO10 = adr;SIO10被赋值后,硬件会自动发送从机地址,注意这里是硬件自动发送,而不体现在代码之中,所以找后面代码,没有发送从机地址的代码。

        等待硬件将从机地址发送完毕并接收到ACK后,才会自动进入中断,这个中断函数具体是__interrupt static void r_iic10_interrupt(),在r_cg_serial_user.c里面,这个函数更为复杂,里面包含了发送/接收字节、应答、应答错误处理、发送字节结束处理等内容。

        *这里注意中断进入的时机,当主程序运行完R_IIC10_Master_Send()最后一行SIO10 = adr;后,并不是立刻进入中断,而是先退出这个函数,直接运行主程序的其他语句,等到硬件发送完从机地址并收到ACK才会进入中断,中间会直接运行主程序的其他代码!

2.1.4 R_IIC10_Master_Receive()

        与R_IIC10_Master_Send()类似,不多讲了,也是要进入__interrupt static void r_iic10_interrupt()。

2.1.5 R_IIC10_StartCondition()

        用来生成一个开始时序。

2.1.6 R_IIC10_StopCondition()

        用来生成一个结束时序。

2.1.7 r_iic10_callback_master_receiveend()

        主机接收完毕,开始运行这个回调函数,初始化后里面是空的,等用户添加需要的代码。

3、创建MyM24C02读写模块

3.1 MyM24C02.h

        这里新建.c和.h文件,将M24C02读写功能完整时序封装起来,方便后续使用。

3.2 MyM24C02.c

        

3.2.1 指定地址写

        完整的写时序是,Start-->发送从机地址-->从机ACK-->发送寄存器地址-->从机ACK-->要写入的1个字节-->从机ACK-->Stop。

        R_IIC10_Master_Send()的第一个字节其实相当于发送寄存器地址,这里定义发送函数时,将第一个字节隔离出来,使意图更明显。

        在R_IIC10_Master_Send()发送前,让发送完毕标志位tx_end置0,然后再在发送完毕的中断回调函数中添加Stop时序,并将标志位置1。

        为什么需要while(!tx_end)?这个就是用来等待从机ACK的,如果不加,那么会像上文提到的,运行到R_IIC10_Master_Send()最后一行SIO10 = adr;后,并不是立刻进入中断,而是先退出这个函数,直接运行主程序的其他语句。

        最后的delayms(5),是因为从机(M24C02)ACK后,需要5ms时间将接收到的若干字节烧录进内存里,如果不等待并执行其他程序,M24C02会直接执行其他程序而中断烧录。

        还需要注意的是M24C02一次最多写16个字节,传输多了不会将多余的写进内存。

3.2.2 指定地址读

        先指定地址写,发送完寄存器地址后,就发送Stop时序(中断回调函数中),然后重新发送读指令,就可以了。

        正常来讲,先指定地址写,发送完寄存器地址后,应该直接发送Start指令,然后发送当前地址读的时序,这里是因为用的硬件IIC,硬件IIC没有软件IIC那样灵活,主机必须Stop指令后,才会释放SCL线,否则一直置0。

        最后,根据模块手册,读取不用加延时。

3.3 main

        

        M24C02的从机地址在手册中有描述,其中前4位是固定的1010,后四位需要看E0~E2的电平。这个需要查看开发板的原理图。

        开发板原理图中的A0~A2都是接地,也就是0,其实对应了E0~E2(不知道这里是原理图写错了还是有别的原因),读写位读的话是1,写的话是0。

        最终得到写地址是1010 0000(0xA0),读地址0xA1。

        注意到发送函数中,这一步&=0x FE(1111 1110),所以从机地址写0xA0 /0xA1都会自动转化为0xA0

        同样的,接收函数中

        就是将从机地址0xA0 /0xA1都转化为0xA1。

        调试运行程序,打几个断点,然后全速运行,每次运行Tx_buffer/Rx_buffer都会有变化。

        

三、遇到的坑

        我之前写好的中文注释,第二次打开文件,都变成乱码了,搜了好半天才发现要取消勾选这里的自动识别文字编码,系统识别错了以后就会乱码。

        还有一点就是M24C02一次最多写入16个字节,这点在手册里面有提到。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐