【简介】
在前两篇(s32k146适配zephyr(一)添加board )(s32k146适配zephyr(二)适配板级别差异),我们已经将本地的S32K146 的开发板添加到zephyr 系统,我们基于此基础上继续使用I2C设备驱动。zephyr 的驱动开发有点像linux 下驱动开发,因为驱动中已经支持了S32K 的I2C总线驱动,我们需要做的是配置DTS 中的I2C节点,开启I2C总线的驱动就可以使用zephyr 的I2C驱动。对S32K146 LPI2C的说明可以查看之前的该帖子(使用S32DS适配I2C接口),在此就不重复赘述。
【I2C总线设备树配置】
S32K146 芯片内部有集成了一个LPI2C的硬件I2C模块,对应的设备节点在\zephyr\dts\arm\nxp\nxp_s32k1xx.dtsi 通用的dtsi文件中描述如下:
lpi2c0: i2c@40066000 {
compatible = "nxp,lpi2c";
clock-frequency = <I2C_BITRATE_STANDARD>;
#address-cells = <1>;
#size-cells = <0>;
reg = <0x40066000 0x1000>;
interrupts = <24 0>;
clocks = <&clock NXP_S32_LPI2C0_CLK>;
status = "disabled";
};再次配置中status 属性是关闭的,我们需要在我们的board 对应的DTS 配置中重写该属性开启I2C设备树节点,对应我们的board下的DTS中引用 lpi2c0 设备并更新对应的属性信息。
&lpi2c0 {
pinctrl-0 = <&lpi2c0_default>;
pinctrl-names = "default";
scl-gpios = <&gpioa 3 GPIO_ACTIVE_HIGH>;
sda-gpios = <&gpioa 2 GPIO_ACTIVE_HIGH>;
status = "okay";
};大家看到这不知是否有个疑问?这里面的配置项目代表的含义是什么,我看到这的时候是有这个疑问的,带着这个疑问查看了官方的nxp,lpi2c 的节点说明,官方的链接(https://docs.zephyrproject.org/latest/build/dts/api/bindings/i2c/nxp%2Clpi2c.html#dtbinding-nxp-lpi2c)有对应的属性描述,我们截取了部分的属性说明如下:

除了上述I2C的节点配置外,我们还需要在PIN_CTRL节点中配置GPIO为I2C功能,对应PIN_CTRL配置如下
&pinctrl {
lpuart1_default: lpuart1_default {
group0 {
pinmux = <LPUART1_RX_PTC8>, <LPUART1_TX_PTC9>;
drive-strength = "low";
};
};
lpi2c0_default: lpi2c0_default {
group1 {
pinmux = <LPI2C0_SDA_PTA2>, <LPI2C0_SCL_PTA3>;
drive-strength = "low";
};
};
};【I2C驱动配置】
我们在上面更新了DTS相关的I2C配置,我们只需要在zephyr 的工程config 中开启I2C驱动就可以将I2C驱动编译到系统中,在应用\zephyr\samples\drivers\uart\shell\prj.conf 文件中添加I2C总线驱动配置。

【测试代码】
更新好上述配置后,我们可以就可以在代码中添加如下测试代码验证I2C总线驱动,在zephyr 中i2c设备驱动通过i2c_msg结构来对总线进行读写访问。

i2c_transfer() 函数将上述的i2c_msg结构信息发送到总线。

我们编写测试代码对总线的地址范围进行扫描遍历,对应的节点是否有应答来确认总线上的节点数目信息,该测试在之前使用S32DS配置I2C总线时使用过(使用S32DS适配I2C接口),只要修改底层I2C驱动访问接口即可。我们使用zephyr I2C驱动函数访问I2C对应修改如下。
I2C 传输使用
#include <stdio.h>
#include "littleshell.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <ringbuffer.h>
#include <zephyr/drivers/gpio.h>
#include <string.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
/********************************************************************************************************
* Private Function Declarations *
*******************************************************************************************************/
static int i2c_probe(const struct device *dev,char addr)
{
uint8_t data = 0;
static struct i2c_msg msg;
int ret = -1;
msg.len = 1;
msg.buf = &data;
msg.flags = I2C_MSG_WRITE;
ret = i2c_transfer(dev,&msg,1,addr);
if(ret == 0)
{
msg.len = 1;
msg.buf = &data;
msg.flags = I2C_MSG_READ | I2C_MSG_STOP;
ret = i2c_transfer(dev,&msg,1,addr);
}
return ret == 0 ? 1 : 0;
}
static void i2c_scan(const struct device *dev,uint8_t start_addr, uint8_t stop_addr)
{
printf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
uint8_t pos = start_addr <= 0xF? 0x00: start_addr & 0xF;
for(; pos < stop_addr; pos++)
{
if((pos & 0xF) == 0)
{
printf("\r\n%02X: ", pos);
}
if(pos < start_addr)
{
printf(" ");
continue;
}
if( i2c_probe(dev,pos) == 1)
{
printf("%02X", pos);
}
else
{
printf("--");
}
printf(" ");
}
printf("\r\n");
}
unsigned int i2c(char argc,char *argv[])
{
static const struct device * dev;
if(strcmp(argv[1],"scan") == 0)
{
i2c_scan(dev,0x00,0x7f);
}
if(strcmp(argv[1],"init") == 0)
{
dev = device_get_binding("i2c@40066000");
if(dev == NULL)
{
printf("i2c init failed.\r\n");
}
}
return 0;
}【实机验证】
上述测试代码,扫描总线的地址信息输出如下,跟之前验证的结果一致。

上述结果已经能说明I2C 总线驱动已经按照预期工作,我们通过逻辑分析仪查看I2C总线的读写访问功能的波形也是很规整的。
以下是扫描到的0x32地址的设备的完整波形,从结果看是有接收到ACK应答。

以下是扫描0x33地址,没有接收到ACK应答的波形。

适配好总线驱动后,后续我们就可以使用I2C总线设备来驱动板子上的I2C外设了。
27


