功能模塊的硬件介紹
I2C( Inter Integrated Circuit)總線是 PHILIPS 公司開發(fā)的一種半雙工、雙向二線制同步串行總線。I2C 總線傳輸數(shù)據(jù)時(shí)只需兩根信號(hào)線,一根是雙向數(shù)據(jù)線 SDA( serial data),另一根是雙向時(shí)鐘線 SCL( serial clock)。 SPI 總線有兩根線分別用于主從設(shè)備之間接收數(shù)據(jù)和發(fā)送數(shù)據(jù),而 I2C 總線只使用一根線進(jìn)行數(shù)據(jù)收發(fā)。
功能模塊的使用說(shuō)明
工程創(chuàng)建
rtthread的numaker-m2354工程模板是下載的其他大佬修改好了的工程,
工程裁剪
使用ENV工具,輸入指令menuconfig,彈出如下界面。
選擇Hardware Drivers Config
然后選擇On-chip peripheral Drivers
選擇I2c使能
使能I2C1(我使用的是aht10傳感器連接的是SDA和SCL)
使用I2c
官方已經(jīng)很貼心的實(shí)現(xiàn)了I2c的驅(qū)動(dòng)層,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 */
使用官網(wǎng)提供的I2c測(cè)試代碼,設(shè)備名稱為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.
/
/
程序清單:這是一個(gè) I2C 設(shè)備使用例程
例程導(dǎo)出了 i2c_aht10_sample 命令到控制終端
命令調(diào)用格式:i2c_aht10_sample i2c1
命令解釋:命令第二個(gè)參數(shù)是要使用的I2C總線設(shè)備名稱,為空則使用默認(rèn)的I2C總線設(shè)備
程序功能:通過(guò) I2C 設(shè)備讀取溫濕度傳感器 aht10 的溫濕度數(shù)據(jù)并打印
/
#include
#include
#define AHT10_I2C_BUS_NAME "i2c1" / 傳感器連接的I2C總線設(shè)備名稱 /
#define AHT10_ADDR 0x38 / 從機(jī)地址 /
#define AHT10_CALIBRATION_CMD 0xE1 / 校準(zhǔn)命令 /
#define AHT10_NORMAL_CMD 0xA8 / 一般命令 /
#define AHT10_GET_DATA 0xAC / 獲取數(shù)據(jù)命令 */
static struct rt_i2c_bus_device i2c_bus = RT_NULL; / I2C總線設(shè)備句柄 /
static rt_bool_t initialized = RT_FALSE; / 傳感器初始化狀態(tài) /
/ 寫傳感器寄存器 */
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;
/ 調(diào)用I2C設(shè)備接口傳輸數(shù)據(jù) /
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/ 讀傳感器寄存器數(shù)據(jù) */
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;
/ 調(diào)用I2C設(shè)備接口傳輸數(shù)據(jù) */
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); / 發(fā)送命令 /
read_regs(i2c_bus, 6, temp); / 獲取傳感器數(shù)據(jù) /
/ 濕度數(shù)據(jù)轉(zhuǎn)換 */
cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20);
/ 溫度數(shù)據(jù)轉(zhuǎn)換 */
*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總線設(shè)備,獲取I2C總線設(shè)備句柄 */
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)
{
/ 讀取溫濕度數(shù)據(jù) /
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");
}
}
/ 導(dǎo)出到 msh 命令列表中 */
MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample);
編譯下載
在FINSH控制臺(tái)下輸入i2c_aht10_sample獲取溫度濕度
完成模塊功能的演示
觀察溫濕度變化,I2c測(cè)試成功
評(píng)論
查看更多