最近做手表项目用到TWI总线,一个用来驱动oled,一个用来驱动三轴加速度传感器。因为两个模块并的驱动时序不一样,所以分开两个twi总线来驱动它们。这里用到了sdk里面的nrf_drv_twi.c。
一、首先来看看NORDIC的twi总线有哪些特性
Listed here are the main features for TWIM:
• I2C compatible
• 100 kbps, 250 kbps, or 400 kbps
• Support for clock stretching
• EasyDMA
- 兼容I2C协议
- 时钟频率有100k,250k,400k可以选择
- 支持时钟扩展
- 支持简易DMA
二、框架图
三、TWI的时序图
1.写从设备
2.读从设备
(1)
(2)
四、举例
- 这次做计步功能用到了bma423,阅读datasheet的知读写时序如下
1)写时序
写从设备相对简单 :
#define MAX_WRITE_LENGTH 200
static bool twi_write_reg(uint8_t slave_addr, uint8_t reg_addr, uint8_t *write_data, uint16_t len)
{
ret_code_t err_code;
uint8_t data[MAX_WRITE_LENGTH] ;
if(len> MAX_WRITE_LENGTH -1)
{
return false;
}
data[0] = reg_addr;
memcpy(&data[1], write_data, len);
m_xfer_done =false;
err_code = nrf_drv_twi_tx(&m_twi_bma423, slave_addr, data, len+1, false);
APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);
return true;
}
需要注意的是这里的slave_addr是7位的从设备地址,最低位的读写位不需要你管, 观察上图可以知道这里的slvave address是0x18。
另外因为这个函数封装要求把register address和write_data分开,所以这里需要把它们合并成data,再一起发送。
data[0] = reg_addr;
memcpy(&data[1], write_data, len);
err_code = nrf_drv_twi_tx(&m_twi_bma423, slave_addr, data, len+1, false);
2)读时序
读从设备和我们见到的传统时序不太一样,这里要求发从设备地址–>再发寄存器地址–>不发停止位–> 再发从设备地址-- >再发数据。
所以读函数如下
static bool_t twi_read_reg(uint8_t slave_addr, uint8_t reg_addr, uint8_t *read_data, uint16_t len)
{
ret_code_t err_code;
m_xfer_done = false;
err_code = nrf_drv_twi_tx(&m_twi_bma423, slave_addr, ®_addr, 1, true);
APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);
m_xfer_done =false;
err_code = nrf_drv_twi_rx(&m_twi_bma423, slave_addr , read_data, len);
APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);
return true;
}
这里我们看到第一次调用nrf_drv_twi_tx 发送函数,最后一个函数参数是true就是 no_stop为true,没有停止位, 然后再调用nrf_drv_twi_rx接收函数。
其他函数如下:
#define BMA423_SALVE_ADDRESS 0x18
/* TWI instance ID. */
#define TWI_BMA423_INSTANCE_ID 1 //使用twi1
/* TWI instance. */
static const nrf_drv_twi_t m_twi_bma423 = NRF_DRV_TWI_INSTANCE(TWI_BMA423_INSTANCE_ID);
/* Indicates if operation on TWI has ended. */
static volatile bool m_xfer_done = false;
/**
* @brief UART initialization.
*/
static void bsp_bma423_twi_init(void)
{
ret_code_t err_code;
const nrf_drv_twi_config_t twi_bma423_config = {
.scl = PIN_BMA423_SCLK,
.sda = PIN_BMA423_SDA,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = true,
};
err_code = nrf_drv_twi_init(&m_twi_bma423, &twi_bma423_config, twi_handler, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi_bma423);
}
/**
* @brief TWI events handler.
*/
static void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{
switch (p_event->type)
{
case NRF_DRV_TWI_EVT_DONE:
if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
{
//NRF_LOG_INFO("twi rx done");// data_handler(m_sample);
}
else if(p_event->xfer_desc.type == NRF_DRV_TWI_XFER_TX)
{
// NRF_LOG_INFO("twi tx done");
}
m_xfer_done = true;
break;
case NRF_DRV_TWI_EVT_ADDRESS_NACK:
NRF_LOG_INFO("Error event: NACK received after sending the address.");
break;
case NRF_DRV_TWI_EVT_DATA_NACK:
NRF_LOG_INFO("Error event: NACK received after sending a data byte.");
break;
default:
break;
}
}
另外 sdk_config.h中要开启TWI功能
// <e> TWI1_ENABLED - Enable TWI1 instance
//==========================================================
#ifndef TWI1_ENABLED
#define TWI1_ENABLED 1
#endif
欢迎关注个人公众号“低功耗蓝牙技术研究及推广”