嵌入式技术论坛
直播中

贾熹

7年用户 1612经验值
私信 关注
[经验]

怎样使用NK-980IOT I2C串行总线读取BMP180呢

开发环境

软件:RT-Thread Studio
硬件:NK-980IOT开发板

I2C介绍

I2C是一种双线、双向串行总线,为设备之间的数据交换提供了一种简单有效的方法。I2C标准是一种真正的多主总线,包括冲突检测和仲裁,当两个或多个主总线试图同时控制总线时,可防止数据损坏。

共有四套I2C控制器,支持断电唤醒功能。

特性

I2C总线使用两根导线(SDA和SCL)在连接到总线的设备之间传输信息。I2C总线的主要功能包括:

最多支持三个I2C端口
主/从模式
主设备和从设备之间的双向数据传输
多主机模式(无中心主机)
支持标准模式(100kbps)、快速模式(400kbps)和快速模式plus(1Mbps)
在不损坏总线上串行数据的情况下,同时传输主机之间的仲裁
内置14位超时计数器,在I2C总线挂起且定时器超时计数器溢出时请求I2C中断
可编程时钟允许多种速率控制
支持7位寻址和10位寻址模式
支持多地址识别(带掩码选项的四个从地址)
支持断电唤醒功能
支持设置/保持时间可编程

功能框图

2.jpg

基本配置

在测试用使用的I2C2,下面主要介绍I2C2的配置。

时钟源配置:在I2CKEN中启用I2C2外设时钟(CLK_APBCLK1[2])

复位配置:在I2C2RST中重置I2C2控制器(SYS_APBIPRST1[2])

管脚配置:

2.jpg

测试中使用的是PB5/PB7:

2.jpg

功能描述

在I2C总线上,数据在主设备和从设备之间传输。SCL和SDA线路上的数据位以字节为单位同步传输。每个数据字节的长度为8位。每个数据位有一个SCL时钟脉冲,MSB先传输,每个传输的字节后有一个确认位。

在SCL的高位期间对每个位进行采样;因此,SDA线可能仅在SCL的低期发生变化,并且在SCL的高期必须保持稳定。当SCL处于高位时,SDA线上的转换被解释为命令(启动或停止)。有关I2C总线定时的更多详细信息,请参考图6.16-2。

I2C时序图

2.jpg

以上内容均来自芯片的数据手册,数据手册里内容非常全,建议大家在需要时一定要去翻阅。新唐是国产IC里少有的能把手册做那么全的,很给力。

外部电路

测试使用了GY-68模块,模块原理图:

2.jpg

与开发板的连接

2.jpg

工程代码部分

工程配置

先使能I2C2

2.jpg

在软件包里有传感器驱动库可以使用

2.jpg

这个库写的不错,里面有对设备抽象、添加自己的设备等代码,对提高编程能力很有启发和学习价值。

初始化

int rt_hw_bmp180_init(const char name, struct rt_sensor_config cfg)
{
rt_err_t ret = RT_EOK;
rt_sensor_t sensor_baro = RT_NULL, sensor_temp = RT_NULL;
struct rt_sensor_module module = RT_NULL;
struct bmp180_dev bmp180 = RT_NULL;
uint8_t bmbuf[22] = {0};

bmp180 = rt_calloc(1, sizeof(struct bmp180_dev));
if(bmp180 == RT_NULL)
{
      LOG_E("malloc memory failed\r\n");
    ret = -RT_ERROR;
    goto __exit;
}
bmp180->i2c_bus = rt_i2c_bus_device_find(cfg->intf.dev_name);
if(bmp180->i2c_bus == RT_NULL)
{
    LOG_E("i2c bus device %s not found!\r\n", cfg->intf.dev_name);
    ret = -RT_ERROR;
    goto __exit;
}    
module = rt_calloc(1, sizeof(struct rt_sensor_device));
if(module == RT_NULL)
{
      LOG_E("malloc memory failed\r\n");
      ret = -RT_ERROR;
    goto __exit;
}
module->sen[0] = sensor_baro;
module->sen[1] = sensor_temp;
module->sen_num = 2;
/*  barometric pressure sensor register */
{
    sensor_baro = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (sensor_baro == RT_NULL)
    {
          goto __exit;
    }
    rt_memset(sensor_baro, 0x0, sizeof(struct rt_sensor_device));
    sensor_baro->info.type       = RT_SENSOR_CLASS_BARO;
    sensor_baro->info.vendor     = RT_SENSOR_VENDOR_BOSCH;
    sensor_baro->info.model      = "bmp180_baro";
    sensor_baro->info.unit       = RT_SENSOR_UNIT_PA;
    sensor_baro->info.intf_type  = RT_SENSOR_INTF_I2C;
    sensor_baro->info.range_max  = 110000;    /* 1Pa */
    sensor_baro->info.range_min  = 30000;
    sensor_baro->info.period_min = 100;    /* read ten times in 1 second */
    rt_memcpy(&sensor_baro->config, cfg, sizeof(struct rt_sensor_config));
    sensor_baro->ops = &bmp180_ops;
    sensor_baro->module = module;
    ret = rt_hw_sensor_register(sensor_baro, name, RT_DEVICE_FLAG_RDWR, (void*)bmp180);
    if (ret != RT_EOK)
    {
        LOG_E("device register err code: %d", ret);
        goto __exit;
    }
}
/* temperature sensor register */
{
    sensor_temp = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (sensor_temp == RT_NULL)
    {
        goto __exit;
    }
    rt_memset(sensor_temp, 0x0, sizeof(struct rt_sensor_device));
    sensor_temp->info.type       = RT_SENSOR_CLASS_TEMP;
    sensor_temp->info.vendor     = RT_SENSOR_VENDOR_BOSCH;
    sensor_temp->info.model      = "bmp180_temp";
    sensor_temp->info.unit       = RT_SENSOR_UNIT_DCELSIUS;
    sensor_temp->info.intf_type  = RT_SENSOR_INTF_I2C;
    sensor_baro->info.range_max  = 850;    /* 0.1C */
    sensor_baro->info.range_min  = -400;
    sensor_temp->info.period_min = 100;    /* read ten times in 1 second */
    rt_memcpy(&sensor_temp->config, cfg, sizeof(struct rt_sensor_config));
    sensor_temp->ops = &bmp180_ops;
    sensor_temp->module = module;
    ret = rt_hw_sensor_register(sensor_temp, name, RT_DEVICE_FLAG_RDWR, (void*)bmp180);
    if (ret != RT_EOK)
    {
        LOG_E("device register err code: %d", ret);
        goto __exit;
    }
}
/* bmp180 read calc param */
ret = bmp180_read_regs(sensor_baro, BMS_CAL_AC1, bmbuf, 22);
if(ret == RT_EOK)
{
    bmp180->calc_param.ac1 = (bmbuf[0]<<8)|bmbuf[1];
    bmp180->calc_param.ac2 = (bmbuf[2]<<8)|bmbuf[3];
    bmp180->calc_param.ac3 = (bmbuf[4]<<8)|bmbuf[5];
    bmp180->calc_param.ac4 = (bmbuf[6]<<8)|bmbuf[7];
    bmp180->calc_param.ac5 = (bmbuf[8]<<8)|bmbuf[9];
    bmp180->calc_param.ac6 = (bmbuf[10]<<8)|bmbuf[11];
    bmp180->calc_param.b1 = (bmbuf[12]<<8)|bmbuf[13];
    bmp180->calc_param.b2 = (bmbuf[14]<<8)|bmbuf[15];
    bmp180->calc_param.mb = (bmbuf[16]<<8)|bmbuf[17];
    bmp180->calc_param.mc = (bmbuf[18]<<8)|bmbuf[19];
    bmp180->calc_param.md = (bmbuf[20]<<8)|bmbuf[21];
}
else
{
    LOG_E("bmp180 read calc param failed\r\n");
    goto __exit;
}
return RT_EOK;
__exit:
if(sensor_baro)
{
rt_free(sensor_baro);
}

if(sensor_temp)
{
    rt_free(sensor_temp);
}
if(module)
{
     rt_free(module);
}
if (bmp180)
{
    rt_free(bmp180);
}
return ret;
}

### 功能实现
```Plain Text
static void read_baro_entry(void *parameter)
{
    rt_device_t baro_dev = RT_NULL, temp_dev = RT_NULL;
    struct rt_sensor_data baro_data,temp_data;
    rt_size_t res0 = 0, res1 = 1;
    rt_uint8_t chip_id;
    baro_dev = rt_device_find("baro_bmp180");
    if (baro_dev == RT_NULL)
    {
           rt_kprintf("not found baro_bmp180 device\r\n");
        return;
    }
    if (rt_device_open(baro_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
    {
        rt_kprintf("open baro_180 failed\r\n");
        return;
    }
    temp_dev = rt_device_find("temp_bmp180");
    if (temp_dev == RT_NULL)
    {
           rt_kprintf("not found temp_bmp180 device\r\n");
        return;
    }
    if (rt_device_open(temp_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
    {
        rt_kprintf("open temp_bmp180 failed\r\n");
        return;
    }
    rt_device_control(baro_dev, RT_SENSOR_CTRL_SET_ODR, (void *)(1));/* 1Hz read */
    rt_device_control(temp_dev, RT_SENSOR_CTRL_SET_ODR, (void *)(1));/* 1Hz read */
    rt_device_control(temp_dev, RT_SENSOR_CTRL_GET_ID, (void*)&chip_id);
    rt_kprintf("bmp180 chip ID [0x%X]\n", chip_id);
    while (1)
    {
        res0 = rt_device_read(baro_dev, 0, &baro_data, 1);
        res0 = rt_device_read(temp_dev, 0, &temp_data, 1);
        if (res0==0 || res1==0)
        {
            rt_kprintf("read data failed! result is %d,%d\n", res0, res1);
            rt_device_close(baro_dev);
            rt_device_close(temp_dev);
            return;
        }
        else
        {
            rt_kprintf("baro[%dPa],temp[%d.%dC],timestamp[%d]\r\n", baro_data.data.baro, 
                       temp_data.data.temp/10, temp_data.data.temp%10,
                       temp_data.timestamp);
        }
        rt_thread_delay(500);
    }
}

测试结果

2.jpg

3.jpg

总结

RT-THREAD现在支持的开发板和传感器越来越全了,点一点就能完成工程搭建和测试。非常适合快速出产品的项目开发。

新唐的NK-980IOT开发板除了开始上手时不熟悉烧写过程,其他都很容易使用,提供的手册资料非常全,堪比ti、adi等大厂,遇到问题建议大家多翻翻手册。

原作者:sunxh_rk

更多回帖

发帖
×
20
完善资料,
赚取积分