在龙芯1c上使用RT-Thread统一标准的i2c接口
来源:互联网 发布:linux显示grub解决办法 编辑:程序博客网 时间:2024/06/05 17:20
本文首先介绍几个常用的RTT统一的标准的I2C接口,然后以I2C接口的EEPROM芯片AT24C02为例,演示如何在龙芯1C上用RTT统一的I2C接口读取EEPROM芯片AT24C02。
在本文之前,已经写过几篇关于龙芯1C上,模拟I2C和硬件I2C,在裸机程序中和RTT中使用的文章,如下
【龙芯1c库】封装硬件I2C接口和使用示例
http://blog.csdn.net/caogos/article/details/77891546
在RT-Thread上使用龙芯1c库中的硬件I2C接口
http://blog.csdn.net/caogos/article/details/77892951
【龙芯1c库】封装模拟I2C接口和使用示例
http://blog.csdn.net/caogos/article/details/73089406
这几篇文章都是以“龙芯1C库”为背景的,而本文的不同之处在于重点讨论RTT统一标准的I2C接口,在龙芯1C上的移植和使用。
本函数的作用是,向RTT添加注册一个I2C总线。本函数为龙芯1C定制的函数,具体的实现细节放到后面移植部分再讨论。
直接调用一下就行。
有的BSP里面是使用
INIT_DEVICE_EXPORT(ls1c_i2c_init);
的形式调用的,但在龙芯1C上没有这样采用。原因是每个人使用I2C时,可能使用不同的引脚。
下面来详细看看第二个参数的结构体定义
rt_i2c_msg为RTT为I2C封装的抽象概念。一次发送或接收,单独用一个msg来表示。
比如要读取AT24C02上某个地址的数据,先要把该数据的地址“写入”(发送)给AT24C02,然后再读取数据。这里面包含了两个msg,首先是一个写的msg,然后是一个读的msg。
这是一个I2C的驱动,假设想再增加一个呢?道理类似,依葫芦画瓢,把scl和sda的引脚改一下,把I2C总线的名字改一下,基本就可以了。
最后在特别说明一下,目前龙芯的gpio没有开漏输出模式,所以在读取sda值的时候需要先设为输入模式。如下
在本文之前,已经写过几篇关于龙芯1C上,模拟I2C和硬件I2C,在裸机程序中和RTT中使用的文章,如下
【龙芯1c库】封装硬件I2C接口和使用示例
http://blog.csdn.net/caogos/article/details/77891546
在RT-Thread上使用龙芯1c库中的硬件I2C接口
http://blog.csdn.net/caogos/article/details/77892951
【龙芯1c库】封装模拟I2C接口和使用示例
http://blog.csdn.net/caogos/article/details/73089406
这几篇文章都是以“龙芯1C库”为背景的,而本文的不同之处在于重点讨论RTT统一标准的I2C接口,在龙芯1C上的移植和使用。
RTT统一的标准的I2C接口简介
初始化I2C
函数原型
int ls1c_i2c_init(void);
本函数的作用是,向RTT添加注册一个I2C总线。本函数为龙芯1C定制的函数,具体的实现细节放到后面移植部分再讨论。
使用示例
ls1c_i2c_init();
直接调用一下就行。
有的BSP里面是使用
INIT_DEVICE_EXPORT(ls1c_i2c_init);
的形式调用的,但在龙芯1C上没有这样采用。原因是每个人使用I2C时,可能使用不同的引脚。
Find设备
函数原型
/** * This function finds a device driver by specified name. * * @param name the device driver's name * * @return the registered device driver on successful, or RT_NULL on failure. */rt_device_t rt_device_find(const char *name)函数rt_device_find()为RTT中通用的,find设备的函数。其它一些地方也可能会见到。入参为I2C总线的名字。I2C总线的名字在函数ls1c_i2c_init()中,调用函数rt_i2c_bit_add_bus()时注册的I2C总线名字。
使用示例
#define AT24C02_I2C_BUS_NAME ("i2c2")struct rt_i2c_bus_device *at24c02_i2c_bus = RT_NULL;// find设备at24c02_i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(AT24C02_I2C_BUS_NAME);if (RT_NULL == at24c02_i2c_bus){ rt_kprintf("[%s] no i2c device -- am2320!\n", __FUNCTION__); return ;}
收发I2C信息
函数原型
rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num)本函数是I2C收发的核心函数。收发都是调用本函数。具体是收,还是发,第二个入参中有个flags来控制。第一个入参为I2C总线(前面已经使用函数rt_device_find()得到的I2C总线),第三个入参为msg的个数,注意要与第二个参数中的len区别。
下面来详细看看第二个参数的结构体定义
struct rt_i2c_msg{ rt_uint16_t addr; rt_uint16_t flags; rt_uint16_t len; rt_uint8_t *buf;};addr为I2C从机的地址,flags为此msg的一些标志,比如用来标记该msg是向从机读数据,还是写数据。len为此msg中收发数据的长度,buf为此msg收发的具体数据。
rt_i2c_msg为RTT为I2C封装的抽象概念。一次发送或接收,单独用一个msg来表示。
比如要读取AT24C02上某个地址的数据,先要把该数据的地址“写入”(发送)给AT24C02,然后再读取数据。这里面包含了两个msg,首先是一个写的msg,然后是一个读的msg。
使用示例
向AT24C02指定地址写入指定数据
/* * 在指定地址写入一个字节的数据 * @write_addr 地址 * @data 待写入的数据 */void at24c02_write_byte(unsigned char write_addr, unsigned char data){ struct rt_i2c_msg msg[1] = {0}; unsigned char buf[2] = {0}; buf[0] = write_addr; buf[1] = data; msg[0].addr = at24c02_addr; msg[0].flags = RT_I2C_WR; msg[0].buf = buf; msg[0].len = 2; rt_i2c_transfer(at24c02_i2c_bus, msg, 1); return ;}
从AT24C02指定地址读出数据
/* * 从指定地址读出一个字节 * @read_addr 地址 */unsigned char at24c02_read_byte(unsigned char read_addr){ struct rt_i2c_msg msgs[2]; unsigned char data = 0; msgs[0].addr = at24c02_addr; msgs[0].flags = RT_I2C_WR; msgs[0].buf = &read_addr; msgs[0].len = 1; msgs[1].addr = at24c02_addr; msgs[1].flags = RT_I2C_RD; msgs[1].buf = &data; msgs[1].len = 1; rt_i2c_transfer(at24c02_i2c_bus, msgs, 2); return data;}
综合应用示例——在龙芯1C上接I2C接口的EEPROM芯片AT24C02
测试的思路是,在at24c02内,地址为1的地方保存复位次数。每次上电后,先读取当前次数,并打印,然后加一,并保存到at24c02中。为了验证保存正确,在读取一次,并将读取的结果打印出来。
实物图
电路连接为
AT24C02 龙芯1c
VCC ------------------- 3.3V
GND ------------------- GND
SCL ------------------- GPIO57
SDA ------------------ GPIO56
串口打印
测试源码清单
在前面介绍函数rt_i2c_transfer()时,已经把读写eeprom的函数介绍了,读写eeprom是本测试用例中的核心代码,这里就直接把完整的测试代码贴出来(I2C移植的部分代码稍后再讨论)。
application.c
bsp\ls1cdev\applications\application.c
/* * File : application.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006-2012, RT-Thread Develop Team * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rt-thread.org/license/LICENSE * * Change Logs: * Date Author Notes * 2010-06-25 Bernard first version * 2011-08-08 lgnq modified for Loongson LS1B * 2015-07-06 chinesebear modified for Loongson LS1C */#include <rtthread.h>#include "net/synopGMAC.h"#include <lwip/api.h>#include <drivers/i2c.h>#include "../drivers/drv_i2c.h"// 测试用的线程 #define THREAD_TEST_PRIORITY (25) #define THREAD_TEST_STACK_SIZE (4*1024) // 4k #define THREAD_TEST_TIMESLICE (10) #define AT24C02_I2C_BUS_NAME ("i2c2") // 注意与i2c bus初始化函数中的bus name保持一致struct rt_i2c_bus_device *at24c02_i2c_bus = RT_NULL;int at24c02_addr = 0xA0 >> 1; // 地址前7位 struct rt_thread thread_test; ALIGN(8) rt_uint8_t thread_test_stack[THREAD_TEST_STACK_SIZE]; /* * 从指定地址读出一个字节 * @read_addr 地址 */unsigned char at24c02_read_byte(unsigned char read_addr){ struct rt_i2c_msg msgs[2]; unsigned char data = 0; msgs[0].addr = at24c02_addr; msgs[0].flags = RT_I2C_WR; msgs[0].buf = &read_addr; msgs[0].len = 1; msgs[1].addr = at24c02_addr; msgs[1].flags = RT_I2C_RD; msgs[1].buf = &data; msgs[1].len = 1; rt_i2c_transfer(at24c02_i2c_bus, msgs, 2); return data;}/* * 在指定地址写入一个字节的数据 * @write_addr 地址 * @data 待写入的数据 */void at24c02_write_byte(unsigned char write_addr, unsigned char data){ struct rt_i2c_msg msg[1] = {0}; unsigned char buf[2] = {0}; buf[0] = write_addr; buf[1] = data; msg[0].addr = at24c02_addr; msg[0].flags = RT_I2C_WR; msg[0].buf = buf; msg[0].len = 2; rt_i2c_transfer(at24c02_i2c_bus, msg, 1); return ;}// 测试用的线程的入口 void thread_test_entry(void *parameter) { unsigned char read_addr = 1; // 地址 unsigned char count = 0; // 用于计数的变量 // 初始化(添加i2c总线--i2c2) ls1c_i2c_init(); // find设备 at24c02_i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(AT24C02_I2C_BUS_NAME); if (RT_NULL == at24c02_i2c_bus) { rt_kprintf("[%s] no i2c device -- am2320!\n", __FUNCTION__); return ; } // 读 count = at24c02_read_byte(read_addr); rt_kprintf("[%s] last's count=%u\n", __FUNCTION__, count); // 加一,然后写 count++; at24c02_write_byte(read_addr, count); rt_thread_delay(6); // 一定要延时5ms以上 // 读 count = at24c02_read_byte(read_addr); rt_kprintf("[%s] current count=%d\n", __FUNCTION__, count); while (1) { // 间隔3s rt_thread_delay(3 * RT_TICK_PER_SECOND); } } void rt_init_thread_entry(void *parameter){/* initialization RT-Thread Components */rt_components_init(); // 网口EMAC初始化rt_hw_eth_init();}int rt_application_init(void){rt_thread_t tid; rt_err_t result;/* create initialization thread */tid = rt_thread_create("init",rt_init_thread_entry, RT_NULL,4096, RT_THREAD_PRIORITY_MAX/3, 20);if (tid != RT_NULL)rt_thread_startup(tid); // 初始化测试用的线程 result = rt_thread_init(&thread_test, "thread_test", thread_test_entry, RT_NULL, &thread_test_stack[0], sizeof(thread_test_stack), THREAD_TEST_PRIORITY, THREAD_TEST_TIMESLICE); if (RT_EOK == result) { rt_thread_startup(&thread_test); } else { return -1; } return 0;}
把RTT统一的标准的I2C接口移植到龙芯1C上
移植要点
RTT支持硬件I2C和模拟I2C,这里使用的是模拟I2C。大家都知道,其实模拟I2C本身就不难,而RTT又把其中一些非硬件相关的,通用的部分提出来了,那么留给我们移植的工作量就更少了。简单来说就只需要实现读取和设置scl和sda的函数就可以了。对应代码为
static const struct rt_i2c_bit_ops bit_ops = { .data = RT_NULL, .set_sda = ls1c_set_sda, .set_scl = ls1c_set_scl, .get_sda = ls1c_get_sda, .get_scl = ls1c_get_scl, .udelay = ls1c_udelay, .delay_us = 20, // 此值为周期(us) .timeout = 10, // 单位为tick};这个结构体中的函数都实现好后,然后调用函数rt_i2c_bit_add_bus()把bit_ops注册一下就可以了。具体代码为
int ls1c_i2c_init(void){ static struct rt_i2c_bus_device bus = {0}; bus.priv = (void *)&bit_ops; ls1c_i2c_gpio_init(); rt_i2c_bit_add_bus(&bus, "i2c2"); return RT_EOK;}
这是一个I2C的驱动,假设想再增加一个呢?道理类似,依葫芦画瓢,把scl和sda的引脚改一下,把I2C总线的名字改一下,基本就可以了。
最后在特别说明一下,目前龙芯的gpio没有开漏输出模式,所以在读取sda值的时候需要先设为输入模式。如下
static rt_int32_t ls1c_get_sda(void *data){#ifdef LS1C_SET_GPIO_MODE gpio_init(LS1C_I2C_SDA, gpio_mode_input); ls1c_udelay(5);#endif return gpio_get(LS1C_I2C_SDA);}static void ls1c_set_sda(void *data, rt_int32_t state){#ifdef LS1C_SET_GPIO_MODE gpio_init(LS1C_I2C_SDA, gpio_mode_output); ls1c_udelay(5);#endif gpio_set(LS1C_I2C_SDA, state); return ;}其中,对GPIO的操作还是使用的龙芯1C库中的gpio函数(bsp\ls1cdev\libraries\ls1c_gpio.h中)。
源码清单
drv_i2c.c
bsp\ls1cdev\drivers\drv_i2c.c
/* * File : drv_i2c.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Change Logs: * Date Author Notes * 2017-11-14 勤为本 first version */#include <rtthread.h>#include <drivers/i2c.h>#include <drivers/i2c-bit-ops.h>#include "drv_i2c.h"#include "../libraries/ls1c_gpio.h"#include "../libraries/ls1c_delay.h"#define LS1C_I2C_SCL (57) // gpio57#define LS1C_I2C_SDA (56) // gpio56#define LS1C_SET_GPIO_MODEstatic void ls1c_i2c_gpio_init(void){ gpio_init(LS1C_I2C_SCL, gpio_mode_output); gpio_set(LS1C_I2C_SCL, gpio_level_high); gpio_init(LS1C_I2C_SDA, gpio_mode_output); gpio_set(LS1C_I2C_SDA, gpio_level_high); return ;}static void ls1c_udelay(rt_uint32_t us){ delay_us((int)us);}static void ls1c_set_sda(void *data, rt_int32_t state){#ifdef LS1C_SET_GPIO_MODE gpio_init(LS1C_I2C_SDA, gpio_mode_output); ls1c_udelay(5);#endif gpio_set(LS1C_I2C_SDA, state); return ;}static void ls1c_set_scl(void *data, rt_int32_t state){#ifdef LS1C_SET_GPIO_MODE gpio_init(LS1C_I2C_SCL, gpio_mode_output); ls1c_udelay(5);#endif gpio_set(LS1C_I2C_SCL, state); return ;}static rt_int32_t ls1c_get_sda(void *data){#ifdef LS1C_SET_GPIO_MODE gpio_init(LS1C_I2C_SDA, gpio_mode_input); ls1c_udelay(5);#endif return gpio_get(LS1C_I2C_SDA);}static rt_int32_t ls1c_get_scl(void *data){#ifdef LS1C_SET_GPIO_MODE gpio_init(LS1C_I2C_SCL, gpio_mode_input); ls1c_udelay(5);#endif return gpio_get(LS1C_I2C_SCL);}static const struct rt_i2c_bit_ops bit_ops = { .data = RT_NULL, .set_sda = ls1c_set_sda, .set_scl = ls1c_set_scl, .get_sda = ls1c_get_sda, .get_scl = ls1c_get_scl, .udelay = ls1c_udelay, .delay_us = 20, // 此值为周期(us) .timeout = 10, // 单位为tick};int ls1c_i2c_init(void){ static struct rt_i2c_bus_device bus = {0}; bus.priv = (void *)&bit_ops; ls1c_i2c_gpio_init(); rt_i2c_bit_add_bus(&bus, "i2c2"); return RT_EOK;}
感谢阅读!
阅读全文
0 0
- 在龙芯1c上使用RT-Thread统一标准的i2c接口
- 在龙芯1c上使用rt-thread统一标准的spi接口
- 在龙芯1c上使用RT-Thread统一标准的gpio接口
- 在RT-Thread上使用龙芯1c库中的硬件I2C接口
- 在龙芯1c上用RT-Thread上的lwip NETCONN接口实现socket编程
- 在windows上搭建龙芯1c的RT-Thread开发环境
- 在龙芯1c上用RT-Thread打印hello
- 在龙芯1C上移植硬浮点FPU到RT-Thread
- RT-Thread在STM32F100C8上的移植
- stm32在rt-thread上使用uart4实现gps的输出
- uip 移植在rt-thread上的源码
- RT Thread 在STM32f10x 平台上的移植
- stm32在rt-thread上的SysTick(系统定时器)
- stm32在rt-thread上的RTC(实时时钟)
- stm32在rt-thread上的PWR(电源管理)
- RT-thread学习笔记1 I2C设备驱动学习之EEPROM驱动(上)
- 用龙芯1c库在RT-Thread下输出PWM
- RT-Thread LwIP的使用
- 7.4
- 对正整数x、y连续进行求余,直到余数为0,此时非0的除数就是最大公约数。x除以y的余数是r,若r不等于0,则把y作为新的x,r作为新的y,重复x除以y的运算,直到r=0为止,此时y就为所求的最大公约数
- 如何正确地写出单例模式
- Mac 实用操作
- springboot elasticsearch 简单测试类
- 在龙芯1c上使用RT-Thread统一标准的i2c接口
- 【转载】mybaits错误解决:There is no getter for property named 'id' in class 'java.lang.String'
- 多线程和单线程执行效率比较
- 第七题 画梯形
- 强烈推荐大家看这篇文章:iOS开发常用三方库、插件、知名博客等等(特别有用)
- MFC程序发布
- 作为一名蒻幾对2017noip提高的反思与总结
- C# XmlSerializer
- python网络编程之udp