Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
I2C_IIC
[新手试水] LPC1768_Nano3_I2C_EEPROM
发布于 2018-03-25 13:40:34 浏览:3779
订阅该版
* 本帖最后由 wlof 于 2018-3-25 13:40 编辑 * **第四章 i2c eeprom**[align=center]Wlof摘要:本文主要介绍了I2C设备驱动框架,参考STM32的实现代码,编写用于LPC176x的i2c应用,将shell的用户名和密码保存在eeprom中,启动时加载到内存,以便给shell使用。**4.1 起源**在前面的文章和代码中,我们自定义了一个二次认证的权限管理器,其中密码和用户名都是写死的,这个一旦被人知道密码就可以随意玩了,为了方便对密码进行存储修改等管理,将它保存到eeprom中去,程序运行时,从eeprom中加载密码。写这个文章的目的呢,也是为了将LPC176x的那个bsp扩展一下,这里面没有I2C的例子,嘿嘿,不不过我写代码的水平比较差,不好意思提交过去,有需要的朋友可以拿去试用。由于LPC176x的BSP里没有I2C的例子,于是参考了STM32的例子和周立功提供的例程。还参考一个网文,写得不错,也是基于STM32的,推荐给各位。[RT-thread设备驱动组件之IIC总线设备](http://www.cnblogs.com/King-Gentleman/p/4658615.html)【俺的编码水平比较菜,高手们不要见怪呀】**4.2 i2c总线设备文件整理** RT_thread的I2c总线设备代码位于components\drivers\i2c\目录下,如图4-1所示。本文件在使用时,将全部文件都复制出来了,就是俺说的手动添加组件。其中,fm24clxx.c和.h这组文件是自带的eeprom驱动啦,已经实现对eeprom的读写操作。[align=center]![1.png](/uploads/201803/25/133152n4nu6nm63d4hggg5.png)[align=center]图4-1i2c总线设备实现文件 手动添加组件的时候,不要忘记了对应的头文件也添加过来,看名字就应该猜得到头文件是啥了,其位置在components\drivers\include\drivers,如图4-2所示。 [align=center]![2.png](/uploads/201803/25/133153oq1bb82qbz91rfyr.png)[align=center]图4-2i2c总线设备的头文件 把它们复制出来,整理到一个文件夹里面去,也可以分开放,一个实现夹,一个头文件夹,根据个人喜好去搞,如图4-3所示。[align=center]![3.png](/uploads/201803/25/133153d85eahb8jrz2u7mk.png)[align=center]图4-3i2c 总线设备文件整理 **4.3 各文件的作用**** 1、i2c_dev.c**主要实现IIC设备驱动统一接口函数:i2c_bus_device_read(),i2c_bus_device_write(),i2c_bus_device_control()以及rt_i2c_bus_device_device_init()。** 2、i2c_core.c**中实现I2C总线设备注册,以及使用I2C总线进行数据传输,如:rt_i2c_transfer(),rt_i2c_master_send(),rt_i2c_master_recv()。** 3、i2c-bit-ops.c**中主要实现了利用GPIO模拟IIC总线时序的相关接口函数,如:i2c_start(),i2c_restart(),i2c_stop(),i2c_waitack(),i2c_writeb(),i2c_readb(),i2c_send_bytes(),i2c_send_ack_or_nack(),i2c_recv_bytes(),i2c_send_address(),i2c_bit_send_address()等。** 4、fm24clxx.c **主要实现eeprom的读写操作。 **4.4 添加到工程**工程文件只是对真实文件一种组织而已,因此呢,工程里头的文件夹可以以方便查找和定位为准进行组织,本文新建了两个文件夹,如图4-4所示。其中,一个叫**DeviceCore**,用来存放**i2c_dev.c,i2c_core.c,i2c-bit-ops.c**,另一个叫**DeviceDrivers**,用来存放**fm24clxx.c**。[align=center]![4.png](/uploads/201803/25/133154iaeog8gzdzkipygh.png) [align=center]图4-4 这样搞的原因有两个,1、内核文件一般是不用修改的,2、一般设备驱动写好后,也是不用修改的,但是如果测试时,发现问题的话,修改也是有必要的,但是内核的话,像我这样的低水平选手,还是不要动手去修改它比较稳当。而这个驱动文件夹,可能放不同的驱动进来呢,如其他的i2c设备,不是eeprom了。 添加之后,要对包含目录进行设置一下,省得到时候要用到对应函数时,包含头文件报错,如图4-5所示。这个地方是要加头文件目录的,如果复制得到处都是的话,这里要加很多进来,所以呢,最好不要搞太多,不然这里很长呢。[align=center]![5.png](/uploads/201803/25/133154nmco2inpl1wwwroi.png)[align=center]图4-5添加头文件检索路径 **4.5 编译出错**工程都搞完了,试一下编译会怎么样,报错了哦!定位一下,如图DeveiceDrivers\i2c_core.c(25):error: #5: cannot open source input file"rtdevice.h": No such file or directoryDeveiceDrivers\i2c_dev.c(26):error: #5: cannot open source input file"rtdevice.h": No such file or directoryDeveiceDrivers\i2c-bit-ops.c(25):error: #5: cannot open source input file"rtdevice.h": No such file or directoryDeveiceDrivers\fm24clxx.c(25):error: #5: cannot open source input file"rtdevice.h": No such file or directory [align=center]![6.png](/uploads/201803/25/133155x5x70s0100hx005x.png)[align=center]图4-6 头文件没有找到搞什么鬼?那个头文件在哪,想不想,肯定在组件的include里面,这几个文件都包含它。不喜欢这样,东复制一下西复制一下,打开那个头文件一看,你会失望的,一堆不知道什么东西,如图4-7。[align=center]![7.png](/uploads/201803/25/133156s37fdzbm903r3lbk.png)[align=center]图4-7 让人心烦的头文件这个就是传说中的意大利面条吗?俺是不知道,反正不喜欢这样搞,虽然引用一个头文件就可以,但是俺还是喜欢别人把对应的文件复制过就能用的思想。先打开那几个文件看一下,写法也很认人不解,为什么不把自己的头文件包含进来呢?这不是舍近求远吗? **不喜欢,不喜欢,不喜欢!**明明一个小东西,非要搞那么多头文件进来,吓死人了,而很多头文件你都没有用到,何必呢?【下面,拿出杀朱刀,开始给它做手术】 **4.6 包含头文件修改** 所有人各司其职,不要乱动别人的东西,各自包含自己的头文件去。可是咱也不知道该包含谁呀?这事好办,一共没有添加几个文件过来,查找一下,文件中定义的结构体在什么地方先。【当然啦,不改也可以,全部头文件复制过来,肯定没有错!咱不是喜欢干吗?嘿嘿,瞎搞一下!】 在搞之前先打开头文件看下,发现i2c.h中定义了基本的结构体和函数,而在core.c中又调用了dev.c中的函数,因此,将i2c.h头文件包含到i2c_dev.h中去,别的地方引用i2c_dev.h就好了。其中rtthread.h在i2c.h中进行包含所以,只包含那个单一文件就可以了。 表4-1 修改头文件列表[table] [tr][td=162] 文件名 [/td][td=162]添加包含[/td][td=162]删除包含[/td][td=162]行号[/td][/tr] [tr][td=162]I2c_core.c[/td][td=162][align=right]#include “i2c_dev.h”[/td][td=162][align=right]#include
[/td][td=162][align=right]25[/td][/tr] [tr][td=162]I2c_dev.c[/td][td=162][align=right]#include “i2c_dev.h”[/td][td=162][align=right]#include
[/td][td=162][align=right]26[/td][/tr] [tr][td=162]I2c_dev.h[/td][td=162][align=right]#include "i2c.h"[/td][td=162][align=right]---------[/td][td=162][align=right]29[/td][/tr] [tr][td=162]I2c-bit-ops.c[/td][td=162][align=right]#include "i2c-bit-ops.h"[/td][td=162][align=right]#include
[/td][td=162][align=right]25[/td][/tr] [tr][td=162]i2c-bit-ops.h[/td][td=162][align=right]#include "i2c_dev.h"[/td][td=162][align=right]---------[/td][td=162][align=right]28[/td][/tr] [tr][td=162]fm24clxx.c[/td][td=162][align=right]---------[/td][td=162][align=right]#include
[/td][td=162][align=right]25[/td][/tr] [tr][td=162]fm24clxx.h[/td][td=162][align=right]#include "i2c_dev.h"[/td][td=162][align=right]---------[/td][td=162][align=right]29[/td][/tr] [/table] 按表4-1进行修改后,报错了,怎么回事呢?看一下提示是什么?如图4-8所示。[align=center]![8.png](/uploads/201803/25/133156tmqqvaet4qnqp476.png)[align=center]图4-8提示缺少定义没有定义这个结构体?怎么会呢?这不是操作系统自带的吗?不管那么多,查找一下,看一下在什么地方?其实你如果要是看过文档或是有过操作系统的使用知道的话,一下就明白,这个是由于操作系统可裁剪配置造成的,即有的功能没有开启。【提示看官方文档是终极武器】本文假设没有任何经验,用最笨的方法找到解决的办法,如图4-9,查找**struct rt_mutex**,在查找的提示行中,选择头文件.h的行地查看,发现在rt_def.h中进行了定义,但是发现它上面有一个开关“**#ifdef RT_USING_MUTEX**”,也就是说,如果这个没有定义的话,那么下面的代码就参加编译了,那我们的程序肯定就找到那个结构体了。第一直觉,应当是去配置文件中设置,如果没有直觉的话,怎么办?还是老方法,工程中查找“**RT_USING_MUTEX**”,看一下什么地方有它,如图4-10。查找发现:**xxx\RTE\RTOS tconfig.h(85) : //#define RT_USING_MUTEX**只有这个地方是定义它的,其他地方都是判断的,很显然就是改它嘛,把那个注释号去掉。看到这里,有人一定会说,你那是碰到死老鼠了,要是模板里没有的话,怎么办?嘿嘿,那就更好办了,**参考官方手册,手册里看查找关键字,**没有说一行行去看,算很给面子了。【古人说,条条大路通罗马】 [align=center]![9.png](/uploads/201803/25/133157ua38o0aasck119co.png)[align=center]图4-9查找结构体rt_mutex[align=center]![10.png](/uploads/201803/25/133157fn2amq2qmnx4j6na.png)[align=center]图4-10查找宏定义RT_USING_MUTEX **4.7 再次编译** 再次编译,没有错误了,但是有警告,看一下是什么鬼,如图4-11,说是没有新行呢,OMG,强迫症来了,点进去,打两个回车,再编译。小改动就引来大美观,值得。[align=center]![11.png](/uploads/201803/25/133157jkj7sppyxoopsxjz.png)[align=center]图4-11新行结束警告 **4.8 底层驱动头文件**这个地方,参考stm32f10x这个bsp下的stm32f1_i2c.h进行编写,首先,写一个头文件lpc_i2c.h,个结构体复制过来,将名字变一下,还有对应的I2C接口名也变一下。 ```struct lpc_i2c_bus //这个名字变了一下 { struct rt_i2c_bus_device parent; struct rt_event ev; //这个我还不会用,想删掉的,后来还是留下来了 LPC_I2C_TypeDef *I2C; //这个类型按LPC的类型进行定义 struct rt_i2c_msg *msg; rt_uint32_t msg_cnt; volatile rt_uint32_t msg_ptr; volatile rt_uint32_t dptr; rt_uint32_t wait_stop; }; /* public function list 这里把LPC的I2C接口类型搞过来*/ rt_err_t lpc_i2c_register(LPC_I2C_TypeDef *I2C, rt_uint32_t bitrate, const char * i2c_bus_name);``` 由于这里有rt_event所以,要在rtconfig.h中定义一下**#define RT_USING_EVENT**我在实现中没有使用这个东西,主要是还不会用它,嘿嘿。先实现功能,然后再说优化的事。 **4.9 底层驱动实现文件**这个地方,参考stm32f10x这个bsp下的stm32f1_i2c.c进行编写,由于本人是新玩操作系统,那些操作系统相关的东西不会搞,所以没有像它一样那么多函数。在正式介绍实现文件之前,先要来看一下,那个fm24clxx.c到底是通过什么方式把数据传送过来的,然后我们要怎么将数据传送给它,只有搞清楚了这件事情,后面的工作才有针对性,当然,不理解的话,照别人的实现文件做也是可以的。**4.9.1 fm24clxx是什么情况**找到fm24clxx_write这个函数,看一下数据发送,如图4-12所示。原来它把数据都打包了呢,放到了一个叫msg的结构体里面,结构体如图4-13所示。结合起来看,msg[0].addr这个是设备访问地址,而msg[0].buf这个指向了mem_addr,这个东东是pos,一看名字就知道是写入位置哦。综上所述,**设备地址:msg[0].addr,写入位置:msg[0].buf,写入数据:msg[1].buf,长msg[1].len.**回来看一下那个msg[0].addr是怎么回事,它是从parent.user_data这里搞出来的**cfg = (const struct fm24clxx_config *)fm24clxx->parent.user_data;**这个东西在初始化时加进来的,过去看一下长什么样子,如图4-14。那个size表示的存储器的大小,addr表示设备访问地址,flgs不知道干什么的,用来对齐用的可能是。[align=center]![12.png](/uploads/201803/25/133158bw8dc8jyyycms8wx.png)[align=center]图4-12 fm24clxx.c中的数据发送函数[align=center]![13.png](/uploads/201803/25/133158qvokp6ges2ypxo0e.png)[align=center]图4-13rt_i2c_msg结构体[align=center]![14.png](/uploads/201803/25/133159ajjrzcrfm9aopwj9.png)[align=center]图4-14初始化时添加的私有数据总之,读写函数基本一样,就是标志不同,这个文件的作用就是整理好数据放入msg中,然后调用rt_i2c_transfer进行数据发送。 **4.9.2 rt_i2c_transfer是啥** 读写函数都是调用它,所以呢,只要搞定这个函数就OK了,看一下,它都在搞什么鬼。如图4-15所示,发现它是调用了一个外部传入的函数进行数据发送的。[align=center]![15.png](/uploads/201803/25/133200qugzqqfqbgddff3m.png)[align=center]图4-15tarnsfer调用的函数 它是调用了总线设备上绑定的发送函数,而这个发送函数是在总线初始化时,应当加入进来的呢,所在在底层实现文件编写时,一定要实现这个发送函数。总之,从fm24clxx.c的初始化函数可以看到,要使用它,必须对总线进行初始化先,不然找不到总线设备,而总线初始化重点实现发送函数(接收同一函数)。 **4.9.3 封装3个函数并初始化** 按图4-15所示的结构体,添加3个函数,其中第2个和第3个直接返回即可,重点实现发送函数,具体的后面再说,先搞定总体结构。[align=center]![16.png](/uploads/201803/25/133200do460uv5g0foa35f.png)[align=center]图4-16按结构体添加3个函数 然后将这3个函数封装到结构体里面去,等后面的初始化代码调用它,将它们传到设备结构体内。[align=center]![17.png](/uploads/201803/25/133201xdmuwg75m3bge23w.png)[align=center]图4-17把它们关起来[align=center]![18.png](/uploads/201803/25/133202mc53w52yrx3wrxx3.png)[align=center]图4-18初始化其中lpc_i2c_init这个代码直接使用现成代码,参考周立功网上提供的工程写就可以了。【说明,这些工作人家都弄好的,参考复制过来就可以,我们重点实现xfer这个函数就可以了。】 **4.9.4 xfer实现** 俺是小白,不会高大上,只会简单粗暴,上面分析数据、地址都是msg传过来的,这个鬼东西,如果想使用现成的代码的话,好像还挺不好弄的呢,怎么办?搞个中介(别担心人家挣差价了,都是为了生活嘛,嘿嘿),如图4-19所示,水平比较菜,结构体比较大个。[align=center]![19.png](/uploads/201803/25/133202ohhhccxezkj2rz2i.png)[align=center]图4-19中介结构体这样搞之后,要发送的地址、数据、长度等信息都在这一个结构体里面了,方便使用现在的代码,然后开启中断,进行数据发送。[align=center]![20.png](/uploads/201803/25/133203hui1ggn5bq5qiuid.png)[align=center]图4-20发送函数对结构体进行填充 **4.9.5 中断函数**其实真正的内容,在中断函数里面搞,参考周立功提供的nxp例程,如图4-21所示。 [align=center]![21.png](/uploads/201803/25/133203e0p9wm49jm0j0jvz.png)[align=center]图4-21中断函数总图 **4.10 再封装一下** 我们在使用时,通常不想知道下面的硬件是怎么工作的,简单地说,提供一个读,一个写。于是搞一个eeprom.c在这里实现i2c总线初始化,eeprom设备初始,读取函数,写入函数。 [align=center]![22.png](/uploads/201803/25/133203r07q02aszt5o0qxr.png)[align=center]图4-22eeprom.c 这里由于使用了structfm24clxx_device 所以,要把fm24clxx.c中定义的 struct fm24clxx_device的定义放到.h里面去,不然会出错。 **4.11 加载用户名和密码** 假设用户名和密码放在0地址。我们在进入mian之后进行读取吧,按理说,应当是在borad初始化时加进去更合理的呢,但是呢Shell实际上是一个任务,它的优先级都比较低,所以呢,不用担心,只要放到任务的初始化代码中去就可以了。 ```/*----------------- Authentication.c中添加---------------------*/ static __inline void load_auth_dat(char *dst,char *src) { char ch = 0; uint8_t i = 0; for(i=0;i
' ' && ch <= '~')) //非法字符 { break; } } if(i != 0) { rt_memset(&dst[0],0,RT_USER_PWD_SIZE); rt_memcpy(&dst[0],&src[0],i); } } void load_eeprom_pswd(void) { char dat[32] = {0}; rt_eeprom_read(0,dat,sizeof(dat)); load_auth_dat((char*)&rt_username[0],&dat[0]); load_auth_dat((char*)&rt_password[0],&dat[16]); } /*-----------------添加完毕---------------------*/ ```任务初始化部分添加如下代码:```//------------begin-------------// extern void rt_hw_i2c_init(void); extern void rt_hw_eeprom_init(void); extern void load_eeprom_pswd(void); rt_hw_i2c_init(); rt_hw_eeprom_init(); load_eeprom_pswd(); //------------beend-------------//``` 文档下载:![RT_Nano_V3初级教程_4 I2C_EEPROM.pdf](/uploads/201803/25/133834bpz4zhxplnllldz9.attach) 工程下载:![CRSytem_RTT3_I2C_EEPROM.7z](/uploads/201803/25/133955wxq71kvmo22xz1ql.attach)
查看更多
1
个回答
默认排序
按发布时间排序
qq_还没想好
2020-08-25
这家伙很懒,什么也没写!
666
撰写答案
登录
注册新账号
关注者
0
被浏览
3.8k
关于作者
wlof
这个家伙不懒,什么也没写
提问
24
回答
64
被采纳
0
关注TA
发私信
相关问题
1
NXP的I2C应该比ST的好用吧
2
Use of I2C device driver
3
关于I2C 驱动问题请教
4
我如何知道这个iic的io配置和我电路设计的是一致的?
5
I2C模拟读操作失败,不知道问什么进不去读函数
6
RTT的I2C有官方文档资料没有
7
求 STM32F103 IIC 自定义IO初始化 代码
8
报一个LPC4008代码中I2C的bug
9
RTOS IIC总线使用
10
关于在RTT中使用STM32 I2C的疑问
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
CherryUSB的bootuf2配置
2
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
3
GD32F450 片内 flash驱动适配
4
STM32H7R7运行CherryUSB
5
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
lizimu
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部