CS+ For CA学习笔记(基于RL78/G13/R5F100LGA开发板)第七课 IIC通讯
好几天没更新了,因为这两天在研究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个字节,这点在手册里面有提到。

更多推荐


所有评论(0)