【NuMaker-M2354试用】I2C测试分享

接口/总线/驱动

1139人已加入

描述

功能模块的硬件介绍

I2C( Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA( serial data),另一根是双向时钟线 SCL( serial clock)。 SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。

功能模块的使用说明

工程创建

rtthread的numaker-m2354工程模板是下载的其他大佬修改好了的工程,

工程裁剪

使用ENV工具,输入指令menuconfig,弹出如下界面。

RTThread

RTThread

选择Hardware Drivers Config

RTThread

然后选择On-chip peripheral Drivers

RTThread

选择I2c使能

RTThread

使能I2C1(我使用的是aht10传感器连接的是SDA和SCL)

RTThread

RTThread

使用I2c

官方已经很贴心的实现了I2c的驱动层,drv_I2c.c。

/************************************************************************//
*

@copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.

SPDX-License-Identifier: Apache-2.0

Change Logs:
Date Author Notes
2020-2-05 HPHuang First version
************************************************************************* * ** /
#include
#ifdef BSP_USING_I2C
#include
#include "NuMicro.h"
/
Private define ---------------------------------------------------------------
/
#define LOG_TAG "drv.i2c"
#define DBG_ENABLE
#define DBG_SECTION_NAME "drv.i2c"
#define DBG_LEVEL DBG_ERROR
#define DBG_COLOR
#include
const rt_uint32_t u32I2C_MASTER_STATUS_START = 0x08UL;
const rt_uint32_t u32I2C_MASTER_STATUS_REPEAT_START = 0x10UL;
const rt_uint32_t u32I2C_MASTER_STATUS_TRANSMIT_ADDRESS_ACK = 0x18UL;
const rt_uint32_t u32I2C_MASTER_STATUS_TRANSMIT_ADDRESS_NACK = 0x20UL;
const rt_uint32_t u32I2C_MASTER_STATUS_TRANSMIT_DATA_ACK = 0x28UL;
const rt_uint32_t u32I2C_MASTER_STATUS_TRANSMIT_DATA_NACK = 0x30UL;
const rt_uint32_t u32I2C_MASTER_STATUS_ARBITRATION_LOST = 0x38UL;
const rt_uint32_t u32I2C_MASTER_STATUS_RECEIVE_ADDRESS_ACK = 0x40UL;
const rt_uint32_t u32I2C_MASTER_STATUS_RECEIVE_ADDRESS_NACK = 0x48UL;
const rt_uint32_t u32I2C_MASTER_STATUS_RECEIVE_DATA_ACK = 0x50UL;
const rt_uint32_t u32I2C_MASTER_STATUS_RECEIVE_DATA_NACK = 0x58UL;
const rt_uint32_t u32I2C_MASTER_STATUS_BUS_ERROR = 0x00UL;
const rt_uint32_t u32I2C_MASTER_STATUS_BUS_RELEASED = 0xF8UL;
/* Private typedef --------------------------------------------------------------* /
typedef struct _nu_i2c_bus
{
struct rt_i2c_bus_device parent;
I2C_T I2C;
struct rt_i2c_msg msg;
char device_name;
} nu_i2c_bus_t;
/
Private variables ------------------------------------------------------------
/
#ifdef BSP_USING_I2C0
#define I2C0BUS_NAME "i2c0"
static nu_i2c_bus_t nu_i2c0 =
{
.I2C = I2C0,
.device_name = I2C0BUS_NAME,
};
#endif /
BSP_USING_I2C0 /
#ifdef BSP_USING_I2C1
#define I2C1BUS_NAME "i2c1"
static nu_i2c_bus_t nu_i2c1 =
{
.I2C = I2C1,
.device_name = I2C1BUS_NAME,
};
#endif /
BSP_USING_I2C1 /
#ifdef BSP_USING_I2C2
#define I2C2BUS_NAME "i2c2"
static nu_i2c_bus_t nu_i2c2 =
{
.I2C = I2C2,
.device_name = I2C2BUS_NAME,
};
#endif /
BSP_USING_I2C2 /
/
Private functions ------------------------------------------------------------* /
#if (defined(BSP_USING_I2C0) || defined(BSP_USING_I2C1) || defined(BSP_USING_I2C2))
static rt_size_t nu_i2c_mst_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num);
static const struct rt_i2c_bus_device_ops nu_i2c_ops =
{
.master_xfer = nu_i2c_mst_xfer,
.slave_xfer = NULL,
.i2c_bus_control = NULL,
};
static rt_err_t nu_i2c_configure(nu_i2c_bus_t *bus)
{
RT_ASSERT(bus != RT_NULL);
bus->parent.ops = &nu_i2c_ops;
I2C_Open(bus->I2C, 100000);
return RT_EOK;
}
static inline rt_err_t nu_i2c_wait_ready_with_timeout(nu_i2c_bus_t *bus)
{
rt_tick_t start = rt_tick_get();
while (!(bus->I2C->CTL0 & I2C_CTL0_SI_Msk))
{
if ((rt_tick_get() - start) > bus->parent.timeout)
{
LOG_E("ni2c: timeout!n");
return -RT_ETIMEOUT;
}
}
return RT_EOK;
}
static inline rt_err_t nu_i2c_send_data(nu_i2c_bus_t *nu_i2c, rt_uint8_t data)
{
I2C_SET_DATA(nu_i2c->I2C, data);
I2C_SET_CONTROL_REG(nu_i2c->I2C, I2C_CTL_SI);
return nu_i2c_wait_ready_with_timeout(nu_i2c);
}
static rt_err_t nu_i2c_send_address(nu_i2c_bus_t *nu_i2c,
struct rt_i2c_msg msg)
{
rt_uint16_t flags = msg->flags;
rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
rt_uint8_t addr1, addr2;
rt_err_t ret;
if (flags & RT_I2C_ADDR_10BIT)
{
nu_i2c->I2C->CTL1 |= I2C_CTL1_ADDR10EN_Msk;
addr1 = 0xf0 | ((msg->addr >> 7) & 0x06);
addr2 = msg->addr & 0xff;
LOG_D("address1: %d, address2: %dn", addr1, addr2);
ret = nu_i2c_send_data(nu_i2c, addr1);
if (ret != RT_EOK) /
for timeout condition /
return -RT_EIO;
if ((I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_TRANSMIT_ADDRESS_ACK) && !ignore_nack)
{
LOG_E("NACK: sending first address failedn");
return -RT_EIO;
}
ret = nu_i2c_send_data(nu_i2c, addr2);
if (ret != RT_EOK) /
for timeout condition /
return -RT_EIO;
if ((I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_TRANSMIT_ADDRESS_ACK) && !ignore_nack)
{
LOG_E("NACK: sending second address failedn");
return -RT_EIO;
}
if (flags & RT_I2C_RD)
{
LOG_D("send repeated START signaln");
I2C_SET_CONTROL_REG(nu_i2c->I2C, I2C_CTL_STA_SI);
ret = nu_i2c_wait_ready_with_timeout(nu_i2c);
if (ret != RT_EOK) /
for timeout condition /
return -RT_EIO;
if ((I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_REPEAT_START) && !ignore_nack)
{
LOG_E("sending repeated START failedn");
return -RT_EIO;
}
addr1 |= 0x01;
ret = nu_i2c_send_data(nu_i2c, addr1);
if (ret != RT_EOK) /
for timeout condition /
return -RT_EIO;
if ((I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_RECEIVE_ADDRESS_ACK) && !ignore_nack)
{
LOG_E("NACK: sending read address failedn");
return -RT_EIO;
}
}
}
else
{
/
7-bit addr /
addr1 = msg->addr << 1;
if (flags & RT_I2C_RD)
addr1 |= 1;
/
Send device address /
ret = nu_i2c_send_data(nu_i2c, addr1); /
Send Address /
if (ret != RT_EOK) /
for timeout condition */
return -RT_EIO;
if ((I2C_GET_STATUS(nu_i2c->I2C)
!= ((flags & RT_I2C_RD) ? u32I2C_MASTER_STATUS_RECEIVE_ADDRESS_ACK : u32I2C_MASTER_STATUS_TRANSMIT_ADDRESS_ACK))
&& !ignore_nack)
{
LOG_E("sending address failedn");
return -RT_EIO;
}
}
return RT_EOK;
}
static rt_size_t nu_i2c_mst_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
struct rt_i2c_msg msg;
nu_i2c_bus_t nu_i2c;
rt_size_t i;
rt_uint32_t cnt_data;
rt_uint16_t ignore_nack;
rt_err_t ret;
RT_ASSERT(bus != RT_NULL);
nu_i2c = (nu_i2c_bus_t ) bus;
nu_i2c->msg = msgs;
nu_i2c->I2C->CTL0 |= I2C_CTL0_STA_Msk | I2C_CTL0_SI_Msk;
ret = nu_i2c_wait_ready_with_timeout(nu_i2c);
if (ret != RT_EOK) /
for timeout condition /
{
rt_set_errno(-RT_ETIMEOUT);
return 0;
}
if (I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_START)
{
i = 0;
LOG_E("Send START Failed");
return i;
}
for (i = 0; i < num; i++)
{
msg = &msgs[i];
ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
if (!(msg->flags & RT_I2C_NO_START))
{
if (i)
{
I2C_SET_CONTROL_REG(nu_i2c->I2C, I2C_CTL_STA_SI);
ret = nu_i2c_wait_ready_with_timeout(nu_i2c);
if (ret != RT_EOK) /
for timeout condition /
break;
if (I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_REPEAT_START)
{
i = 0;
LOG_E("Send repeat START Fail");
break;
}
}
if ((RT_EOK != nu_i2c_send_address(nu_i2c, msg))
&& !ignore_nack)
{
i = 0;
LOG_E("Send Address Fail");
break;
}
}
if (nu_i2c->msg[i].flags & RT_I2C_RD) /
Receive Bytes /
{
rt_uint32_t do_rd_nack = (i == (num - 1));
for (cnt_data = 0 ; cnt_data < (nu_i2c->msg[i].len) ; cnt_data++)
{
do_rd_nack += (cnt_data == (nu_i2c->msg[i].len - 1)); /
NACK after last byte for hardware setting /
if (do_rd_nack == 2)
{
I2C_SET_CONTROL_REG(nu_i2c->I2C, I2C_CTL_SI);
}
else
{
I2C_SET_CONTROL_REG(nu_i2c->I2C, I2C_CTL_SI_AA);
}
ret = nu_i2c_wait_ready_with_timeout(nu_i2c);
if (ret != RT_EOK) /
for timeout condition /
break;
if (nu_i2c->I2C->CTL0 & I2C_CTL_AA)
{
if (I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_RECEIVE_DATA_ACK)
{
i = 0;
break;
}
}
else
{
if (I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_RECEIVE_DATA_NACK)
{
i = 0;
break;
}
}
nu_i2c->msg[i].buf[cnt_data] = nu_i2c->I2C->DAT;
}
}
else /
Send Bytes /
{
for (cnt_data = 0 ; cnt_data < (nu_i2c->msg[i].len) ; cnt_data++)
{
/
Send register number and MSB of data /
ret = nu_i2c_send_data(nu_i2c, (uint8_t)(nu_i2c->msg[i].buf[cnt_data]));
if (ret != RT_EOK) /
for timeout condition /
break;
if (I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_TRANSMIT_DATA_ACK
&& !ignore_nack
) /
Send aata and get Ack /
{
i = 0;
break;
}
}
}
}
I2C_STOP(nu_i2c->I2C);
RT_ASSERT(I2C_GET_STATUS(nu_i2c->I2C) == u32I2C_MASTER_STATUS_BUS_RELEASED);
if (I2C_GET_STATUS(nu_i2c->I2C) != u32I2C_MASTER_STATUS_BUS_RELEASED)
{
i = 0;
}
nu_i2c->msg = RT_NULL;
nu_i2c->I2C->CTL1 = 0; / clear all sub modes like 10 bit mode /
return i;
}
#endif
/
Public functions -------------------------------------------------------------
/
int rt_hw_i2c_init(void)
{
rt_err_t ret = RT_ERROR;
#if defined(BSP_USING_I2C0)
SYS_UnlockReg();
SYS_ResetModule(I2C0_RST);
SYS_LockReg();
nu_i2c_configure(&nu_i2c0);
ret = rt_i2c_bus_device_register(&nu_i2c0.parent, nu_i2c0.device_name);
RT_ASSERT(RT_EOK == ret);
#endif /
BSP_USING_I2C0 /
#if defined(BSP_USING_I2C1)
SYS_UnlockReg();
SYS_ResetModule(I2C1_RST);
SYS_LockReg();
nu_i2c_configure(&nu_i2c1);
ret = rt_i2c_bus_device_register(&nu_i2c1.parent, nu_i2c1.device_name);
RT_ASSERT(RT_EOK == ret);
#endif /
BSP_USING_I2C1 /
#if defined(BSP_USING_I2C2)
SYS_UnlockReg();
SYS_ResetModule(I2C2_RST);
SYS_LockReg();
nu_i2c_configure(&nu_i2c2);
ret = rt_i2c_bus_device_register(&nu_i2c2.parent, nu_i2c2.device_name);
RT_ASSERT(RT_EOK == ret);
#endif /
BSP_USING_I2C2 /
return ret;
}
INIT_DEVICE_EXPORT(rt_hw_i2c_init);
#endif /
BSP_USING_I2C */
使用官网提供的I2c测试代码,设备名称为i2c1

/*

Copyright (c) 2006-2018, RT-Thread Development Team

SPDX-License-Identifier: Apache-2.0

Change Logs:
Date Author Notes
2018-08-15 misonyo first implementation.
/
/

程序清单:这是一个 I2C 设备使用例程
例程导出了 i2c_aht10_sample 命令到控制终端
命令调用格式:i2c_aht10_sample i2c1
命令解释:命令第二个参数是要使用的I2C总线设备名称,为空则使用默认的I2C总线设备
程序功能:通过 I2C 设备读取温湿度传感器 aht10 的温湿度数据并打印
/
#include
#include
#define AHT10_I2C_BUS_NAME "i2c1" /
传感器连接的I2C总线设备名称 /
#define AHT10_ADDR 0x38 /
从机地址 /
#define AHT10_CALIBRATION_CMD 0xE1 /
校准命令 /
#define AHT10_NORMAL_CMD 0xA8 /
一般命令 /
#define AHT10_GET_DATA 0xAC /
获取数据命令 */
static struct rt_i2c_bus_device i2c_bus = RT_NULL; / I2C总线设备句柄 /
static rt_bool_t initialized = RT_FALSE; /
传感器初始化状态 /
/
写传感器寄存器 */
static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data)
{
rt_uint8_t buf[3];
struct rt_i2c_msg msgs;
buf[0] = reg; //cmd
buf[1] = data[0];
buf[2] = data[1];
msgs.addr = AHT10_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = 3;
/
调用I2C设备接口传输数据 /
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/
读传感器寄存器数据 */
static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t buf)
{
struct rt_i2c_msg msgs;
msgs.addr = AHT10_ADDR;
msgs.flags = RT_I2C_RD;
msgs.buf = buf;
msgs.len = len;
/
调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static void read_temp_humi(float *cur_temp, float cur_humi)
{
rt_uint8_t temp[6];
write_reg(i2c_bus, AHT10_GET_DATA, 0); /
发送命令 /
read_regs(i2c_bus, 6, temp); /
获取传感器数据 /
/
湿度数据转换 */
cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20);
/
温度数据转换 */
*cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;
}
static void aht10_init(const char name)
{
rt_uint8_t temp[2] = {0, 0};
/
查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!n", name);
}
else
{
write_reg(i2c_bus, AHT10_NORMAL_CMD, temp);
rt_thread_mdelay(400);
temp[0] = 0x08;
temp[1] = 0x00;
write_reg(i2c_bus, AHT10_CALIBRATION_CMD, temp);
rt_thread_mdelay(400);
initialized = RT_TRUE;
}
}
static void i2c_aht10_sample(int argc, char argv[])
{
float humidity, temperature;
char name[RT_NAME_MAX];
humidity = 0.0;
temperature = 0.0;
if (argc == 2)
{
rt_strncpy(name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(name, AHT10_I2C_BUS_NAME, RT_NAME_MAX);
}
if (!initialized)
{
/
传感器初始化 /
aht10_init(name);
}
if (initialized)
{
/
读取温湿度数据 /
read_temp_humi(&temperature, &humidity);
rt_kprintf("read aht10 sensor humidity : %d.%d %%n", (int)humidity, (int)(humidity * 10) % 10);
rt_kprintf("read aht10 sensor temperature: %d.%d n", (int)temperature, (int)(temperature * 10) % 10);
}
else
{
rt_kprintf("initialize sensor failed!n");
}
}
/
导出到 msh 命令列表中 */
MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample);

编译下载

在FINSH控制台下输入i2c_aht10_sample获取温度湿度

RTThread

完成模块功能的演示

观察温湿度变化,I2c测试成功

RTThread

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分