当前位置: 首页 > news >正文

Hi3061M开发板初测2——IIC通信-OLED(两种实现方法)

目录

    • 前言
    • 案例源码
    • 我的实现
    • 测试
    • OLED点亮
    • 踩坑与调试
      • 吐槽下
      • 测试问题
        • 接线错误
        • 从地址错误
    • 另一种方法---原始库函数

前言

测试过环境的搭建,IO口的基本配置和使用后,开始测试IIC通信。

案例源码

IDE中提供IIC的通信案例,将在此基础上进行修改。

下面是代码主要功能注释,案例是阻塞通信的,基于24c64芯片,eeprom,存储芯片
宏定义了设备的读写地址,用到我们的IIC通信模块需要进行对应的修改

#define DEV_24C64_ADDRESS_WRITE            0xA0
#define DEV_24C64_ADDRESS_READ             0xA1
static BASE_StatusType Sample24c64ReadData(unsigned int addr, unsigned char *buffer, unsigned int len)
{BASE_StatusType ret = BASE_STATUS_OK;/* Define variables for internal use. */unsigned char tempAddr[I2C_SAMPLE_24C64_ADDR_SIZE];unsigned int currentLen = len;unsigned int currentAddr = addr;unsigned int tempReadLen;unsigned char *tempBuffer = buffer;/* Start read data from the 24c64 eeprom. */while (1) {if (currentLen == 0) {break;}/* Set the memory address of eeprom. */tempAddr[0] = (currentAddr >> I2C_SAMPLE_24C64_ADDRESS_POS) & I2C_SAMPLE_24C64_ADDRESS_MASK;tempAddr[1] = currentAddr & I2C_SAMPLE_24C64_ADDRESS_MASK;//这里就是将16位的地址分为两个8位,用于后面发送,而我们一般的其他IIC往往是8位的寄存器地址tempReadLen = currentLen;if (currentLen > I2C_SAMPLE_24C64_OPT_ONCE_LEN) {tempReadLen = I2C_SAMPLE_24C64_OPT_ONCE_LEN;}//这里控制一次读取的长度,限制最多255/* Send the memory address of eeprom. */ret = HAL_I2C_MasterWriteBlocking(&g_i2c0, DEV_24C64_ADDRESS_WRITE, tempAddr,I2C_SAMPLE_24C64_ADDR_SIZE, I2C_SAMPLE_MAX_TIMEOUT);//调用hal库的主机阻塞写函数,输入参数位,IIC接口,从设备地址,写数据(这里是地址),写数据长度(多少字节),超时设定,,默认即可//这里是一个读函数,所以需要告诉从设备读的哪个地址,需要写操作if (ret != BASE_STATUS_OK) {DBG_PRINTF("LINE:%d,Read Data Fail,ret:%d\r\n", __LINE__, ret);return ret;}/* Read data from eeprom. */ret = HAL_I2C_MasterReadBlocking(&g_i2c0, DEV_24C64_ADDRESS_READ, tempBuffer,tempReadLen, I2C_SAMPLE_MAX_TIMEOUT);//具体的读实现函数,参数,IIC接口,从设备地址读地址,读存储数组、读长度,超时if (ret != BASE_STATUS_OK) {DBG_PRINTF("LINE:%d,Read Data Fail,ret:%d\r\n", __LINE__, ret);return ret;}/* Updata the destAddress, srcAddress and len. */currentAddr += tempReadLen;currentLen -= tempReadLen;tempBuffer += tempReadLen;//超过了一次读取长度255,分多次读取进行拼接}return ret;
}

另外介绍的是读写函数的具体实现,原理步骤都是一致的,看写函数

ret = HAL_I2C_MasterWriteBlocking(&g_i2c0, DEV_24C64_ADDRESS_WRITE, tempAddr,I2C_SAMPLE_24C64_ADDR_SIZE, I2C_SAMPLE_MAX_TIMEOUT);
ret = HAL_I2C_MasterReadBlocking(&g_i2c0, DEV_24C64_ADDRESS_READ, tempBuffer,tempReadLen, I2C_SAMPLE_MAX_TIMEOUT);                                          

这个里面的实现代码就不贴了,就是按IIC时序进行,不过是多加了一些处理,比如判断总线是否占用,然后就是先发送从设备地址,起始信号,读写数据, 终止信号,都封装好了的,不过有些参数确实有时候看不明白,功力太浅,能用就行吧。好像最后的实现是将数据传入读写FIFO,一次一字节。

static BASE_StatusType SetTxFIFODataAndCmd(I2C_Handle *handle, I2C_CmdType cmd, unsigned char data)
{BASE_StatusType ret;unsigned int temp;ret = WaitStatusReady(handle, TX_FIFO_NOT_FULL, I2C_OPERATION_WRITE);if (ret != BASE_STATUS_OK) {return ret;}/* The 8 to 11 bits are the Timing Commands, and the 0 to 7 bits are the write data. */temp = (((unsigned int)cmd << I2C_TXFIFO_CMD_POS) & I2C_TXFIFO_CMD_MASK);temp |= (((unsigned int)data << I2C_TXFIFO_WDATA_POS) & I2C_TXFIFO_WDATA_MASK);handle->baseAddress->I2C_TX_FIFO.reg = temp; /* Sets the data and commands to be sent. */return BASE_STATUS_OK;
}

我的实现

源码是基于24c64芯片芯片的,虽然都是IIC通信,但是没法直接一起来用,毕竟寄存器地址范围不同,一般的IIC传感器之类的设备,就8位地址,尤其是命令类的,一次一字节传输,所以需要进行一些简单的修改简化。

BASE_StatusType IIC_my_ReadData(unsigned int addr, unsigned char *buffer, unsigned int len)
{BASE_StatusType ret = BASE_STATUS_OK;unsigned char tempAddr[I2C_SAMPLE_OLED_ADDR_SIZE];/* Set the memory address of eeprom. */tempAddr[0] = addr;ret = HAL_I2C_MasterWriteBlocking(&g_i2c0, IIC_WRITE, tempAddr,I2C_SAMPLE_OLED_ADDR_SIZE, I2C_SAMPLE_MAX_TIMEOUT);if (ret != BASE_STATUS_OK) {DBG_PRINTF("LINE:%d,Read Data Fail,ret:%d\r\n", __LINE__, ret);return ret;}/* Read data from eeprom. */ret = HAL_I2C_MasterReadBlocking(&g_i2c0, IIC_READ, buffer,len, I2C_SAMPLE_MAX_TIMEOUT);if (ret != BASE_STATUS_OK) {DBG_PRINTF("LINE:%d,Read Data Fail,ret:%d\r\n", __LINE__, ret);return ret;}return ret;
}// SetTxFIFODataAndCmd 一次8位
BASE_StatusType IIC_my_WriteData(unsigned int addr, unsigned char dat, unsigned int len)
{unsigned char tempWrite[I2C_SAMPLE_OLED_PAGE_SIZE + I2C_SAMPLE_OLED_ADDR_SIZE];//写寄存器地址和存储发送数据 通常1+1 2个字节unsigned int tempWriteLen;BASE_StatusType ret = BASE_STATUS_OK;/* Set the memory address of eeprom. */tempWrite[0] = addr;tempWrite[1] = dat;ret = HAL_I2C_MasterWriteBlocking(&g_i2c0, IIC_WRITE, tempWrite,len + I2C_SAMPLE_OLED_ADDR_SIZE, I2C_SAMPLE_MAX_TIMEOUT);//len + I2C_SAMPLE_OLED_ADDR_SIZE ,写的长度加上写的寄存器地址长度,通常1+1 2个字节if (ret != BASE_STATUS_OK) {DBG_PRINTF("LINE:%d,Write Data Fail!,ret:%d\r\n", __LINE__, ret);} return ret;    
}

测试

涉及到IIC的读写,而OLED只有写,所以用一个光强度模块进行了测试,芯片好像是AP3216。

    IIC_my_WriteData(0x00,0x03,1);DBG_PRINTF("write ok! \r\n");BASE_FUNC_DELAY_MS(1000);IIC_my_ReadData(0x00,s,1);DBG_PRINTF("read ok! read value 0x0%d\r\n",(int)s[0]);

输出结果
在这里插入图片描述

OLED点亮

使用OLED需要进行一系列初始化,还有将原来stm32的代码移植过来使用。其实也简单,就是包装换一下就好。

在这里插入图片描述

显示展示:

hello

踩坑与调试

吐槽下

只能说这个东西真的弄了我好久,差点整吐了,一开始代码改好了很快,想测试一下,结果就是超时。
准确来说一开始是找IO口,从提供的文档中翻找,但是他这个开发板为了适应兼容,就文档把IO写的很。。。。。
反正是看的很难受,一方面是mcu板的功能,还有扩展板电机驱动板,还有能接ARDUINO UNO。

虽然但是他这很好,但是文档有点难受,主要还有另一个为了兼容ARDUINO UNO的口还是电机的,IO口做了扩展,就是连接,反正就是电路设计是哪些电阻焊上了,就是对应哪个端口是和这个IO连接的,然后有些没焊的
在这里插入图片描述

但是他文档前面的功能引脚的时候写的又不管是焊上没焊上,是直接都写有这个功能的,但事实是 有时候那个引脚可能没接上mcu对上的io口,就比如我找IIC口的时候,这个I2C0_SCL说是j2.4,IO口是GPIO2_0。

在这里插入图片描述
但是实际上,默认却是没接的,但是他也写,就这么告诉你,然后你去接那个脚发现更本不行
在这里插入图片描述
然后写的不全,这个有那个功能,我使用的,且可以连接的反而不写,这个4_2是IIC0 的SCL口,他不写,巨难受。

在这里插入图片描述

最后还是得看原理图,结合开发的IDE的芯片配置器来看
在这里插入图片描述
顺便解释下原理图
在这里插入图片描述
焊接的是一个0欧的电阻,我也是才知道哈,见识少谅解。主要就是用来连接,NC标号的表示对应的电阻没有焊,当时没有仔细看原理图和实际板子结合去理解其意思,后知后觉。自己的问题。

测试问题

在调试过程中遇到了两个问题,如果有人遇到类似的可以参考。

接线错误

第一个是接线接错了,前面找对应的接线柱找错了,scl接线接错了。
然后软件调试的结果是总线占用,也就是iic还没有开始,起始信号都没有发送就报错结束了。
BASE_StatusType HAL_I2C_MasterWriteBlocking函数中的下面这个函数中
/* The I2C bus is free. */
ret = handle->baseAddress->I2C_FSM_STAT.BIT.i2c_bus_free;
这个标志会为0,表示总线占用,然后下面函数就报超时

    /* Waiting for the i2c bus to be idle. */ret = WaitStatusReady(handle, I2C_BUS_IS_FREE, I2C_SEND_ADDR_STATUS_READ);
从地址错误

前面提到了,每个IIC设备的地址可能是不一样的,比如我这的oled是0x78,AP3216是0x3c。
这个要根据实际地址进行更改。
如果这个地址错误,前面的总线空闲检查、起始信号、从地址发送都会进行且不报错,但是在数据发送的时候会报错。同样是上面函数BASE_StatusType HAL_I2C_MasterWriteBlocking

    /* step3 : Send to slave data */ret = BlockingMasterTxDataOptStepNormal(handle);

具体是BlockingMasterTxDataOptStepNormal()–>>SetTxFIFODataAndCmd–>>WaitStatusReady中的下面部分

        if (handle->baseAddress->I2C_INTR_RAW.reg & I2C_ERROR_BIT_MASK) {SetErrorHandling(handle);return BASE_STATUS_ERROR;}

返回的报错结果BASE_STATUS_ERROR

另一种方法—原始库函数

另外一种不用案例中提供的接口hal库,可以直接用标准库,或者说就是把io口设置为正常的IO使用,调节其高低电平,按IIC时序进行通信,就和原来的stm32进行通信时用库函数一样的,把原来的文件复制过来,修改一下,改下电平变化的函数,也可以点亮OLED。
在这里插入图片描述

不过这个的io口初始化,可能就要自己定义一下了,通过软件生成的可能不太行,因为要分开定义,自动化生成的还是有点呆。

static void GPIO_Init(void)
{HAL_CRG_IpEnableSet(GPIO4_BASE, IP_CLK_ENABLE);g_gpio4_2.baseAddress = GPIO4;g_gpio4_3.baseAddress = GPIO4;g_gpio4_2.pins = GPIO_PIN_2;HAL_GPIO_Init(&g_gpio4_2);HAL_GPIO_SetDirection(&g_gpio4_2, g_gpio4_2.pins, GPIO_OUTPUT_MODE);HAL_GPIO_SetValue(&g_gpio4_2, g_gpio4_2.pins, GPIO_HIGH_LEVEL);HAL_GPIO_SetIrqType(&g_gpio4_2, g_gpio4_2.pins, GPIO_INT_TYPE_NONE);g_gpio4_3.pins = GPIO_PIN_3;HAL_GPIO_Init(&g_gpio4_3);HAL_GPIO_SetDirection(&g_gpio4_3, g_gpio4_3.pins, GPIO_OUTPUT_MODE);HAL_GPIO_SetValue(&g_gpio4_3, g_gpio4_3.pins, GPIO_HIGH_LEVEL);HAL_GPIO_SetIrqType(&g_gpio4_3, g_gpio4_3.pins, GPIO_INT_TYPE_NONE);return;
}

此外oled.h要进行修改,还有相应的头文件要进行删除,对应的头文件也要加入。主要用到的就是io口高低电平设置变化的函数。

#define OLED_SCLK_Clr()     HAL_GPIO_SetValue(&g_gpio4_2, g_gpio4_2.pins, GPIO_LOW_LEVEL)//SCL
#define OLED_SCLK_Set()     HAL_GPIO_SetValue(&g_gpio4_2, g_gpio4_2.pins, GPIO_HIGH_LEVEL)#define OLED_SDIN_Clr()     HAL_GPIO_SetValue(&g_gpio4_3, g_gpio4_3.pins, GPIO_LOW_LEVEL)//SDA
#define OLED_SDIN_Set()     HAL_GPIO_SetValue(&g_gpio4_3, g_gpio4_3.pins, GPIO_HIGH_LEVEL)

以上就是全部内容了,欢迎批评指正,有什么问题欢迎留言讨论。


http://www.mrgr.cn/news/20901.html

相关文章:

  • C/C++:类的编写和使用
  • IEEE TRO 人形机器人遥操作的综述
  • numpy03:numpy广播机制,花式索引取值,统计方法,数组的拆分与合并,线性代数方法
  • Docker构建镜像方法
  • 【动态规划问题(2)】路径问题
  • 中秋好物精选推荐,健康佩戴运动首选!
  • 解决Django会话中的竞态条件
  • 邮件主题模板
  • Flink Forward Asia 2024 议题征集令|探索实时计算新边界
  • APO与SkyWalking、Signoz等产品的不同设计
  • /单元测试
  • CSS学习8[重点]--盒子模型边框、内外边距设定和外边距合并
  • Proxifier代理配置
  • 人工智能主要是学什么的?
  • 极兔速递小程序任务脚本
  • lazada自养号秘籍:一次成号测评环境系统全解析
  • Gin 自带日志系统:深入理解与自定义
  • laravel command 执行自定义命令 choice 以后使用info 中文乱码
  • [论文笔记] LLM大模型剪枝篇——1、调研
  • 使用twilio完成网上拨打电话和发送短信