本帖最后由 lulugl 于 2025-4-14 09:17 编辑
#申请开发板# #申请原创# #有奖活动# @21小跑堂
【前言】
前面我分享了如何搭建基础工程:
【英飞凌PSOC 4000T DIY】进入hellowold之旅 https://bbs.21ic.com/icview-3445992-1-1.html这一篇,我将介绍如何使用CY8CPROTO-040T-MS的串行外设中的I2C外设。我前面学习到了他的串行外设下面可以实现UART、I2C、SPI三个模式,由于这款开发板的IO资源是大部分分配给了电容传器的,所以在UART与I2C分配了同样的IO,所以在开发板上还设计了一个开关SW2进行IO连接的转换。
因此在使用I2C外设时,需要把这个SW2拨到I2C的一侧,这是需要大家注意的。
当然我们也可以使用GPIO来摸拟I2C实现,但是这样就使用不到了他的I2C的一些特有功能了。
【原理图】
如果要使用它他的外设,首先需要了解他的原理图,这样才能正确的把OLED与开发板连接起来。在用户手册中,对扩展接口J24原理图:
在接口中第2脚为GND,第5脚为VCC,经测试电压为3.3可以为OLED提供工作电压。
从原理图的上得到I2C的SDA与SCL分别这P2_3,P2_2:
也与他的数据手册中的pins的复用一致的:
经过上面的分析,那下面就着手如何对他的I2C进行驱动了。
【工程移植】
1、在官方的示例库中有两个工程,分别为I2C的Master和Slave,工程:
2、我们需要驱动的的OLED屏,因此只需要master发送数据即可,所以选用i2c master这个工程做为模版工程即可。
按照前面hello world的工程创建方法,我这里创建了IIC的模版工程,创建好工程如下:
3、拿到模块代码后,首先分析一下他的I2C的工程结构,在工程中有I2C的文件,分别为I2CMaster.c与I2CSlave.c,这两个分别处理作为主、从来处理数据的文件,我们只需要发送数据给OLED所以只需要关心I2CMaster这个文件即可。
4、在master.c中,他首先创建了cy_stc_scb_i2c_master_xfer_config_t 的结构体,并把从机地址给分配为了I2C_SLAVE_ADDR,这是一个宏,因此我们需要把这个宏替换为我们的OLED屏,我这里使用的芯片为ssd1306因此,需要把宏修改为0x3C
/* I2C slave address to communicate with */
#define I2C_SLAVE_ADDR (0x3c)
5、接下来在I2CMaster.c中有WritePacketToEzI2C这个函数,函数的注释如下
/*******************************************************************************
* Function Name: WritePacketToEzI2C
****************************************************************************//**
*
* Summary:
* Buffer is assigned with data to be sent to slave.
* high level PDL library function is used to control I2C SCB to send data to
* EzI2C slave. Errors are handled depend on the return value from the
* appropriate function.
*
* Parameters:
* writebuffer: Command packet buffer pointer
* bufferSize: Size of the packet buffer
*
* Return:
* Status after command is written to slave.
* TRANSFER_ERROR is returned if any error occurs.
* TRANSFER_CMPLT is returned if write is successful.
*
*******************************************************************************/
函数的说明非常明确,我们只需要把需要发送的writebuffer首地址,以及需要发送的地址传入就行了,返回为两个状态,即为TRANSFER_ERROR或者传感完成TRANSFER_CMPLT。
经过上面的从机地址的指定,以及I2C发送数据的功能函数后,我们就可以来实现我们OLED的代码了。
6、在工程的source文件夹下添加两个文件一个为ssd1306_driver.h以及ssd1306_driver.c,编写OLED的发送命令与发送数据的函数:
// 发送命令到 SSD1306
void ssd1306_send_command(uint8_t command) {
uint8_t buffer[2] = {SSD1306_COMMAND_MODE, command};
WritePacketToEzI2C(buffer, 2);
}
// 发送数据到 SSD1306
void ssd1306_send_data(uint8_t data) {
uint8_t buffer[2] = {SSD1306_DATA_MODE, data};
WritePacketToEzI2C(buffer, 2);
}
基实移植OLED就是修改以上的两个函数,即可完成。整体代码如下:
/*
* ssd1306_driver.c
*
* Created on: 2025年4月13日
* Author: liujianhua
*/
#include "cy_pdl.h"
#include "cybsp.h"
#include "I2CMaster.h"
#define SSD1306_I2C_ADDRESS 0x3C
#define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64
#define SSD1306_COMMAND_MODE 0x00
#define SSD1306_DATA_MODE 0x40
#define TRANSFER_ERROR 0
// 发送命令到 SSD1306
void ssd1306_send_command(uint8_t command) {
uint8_t buffer[2] = {SSD1306_COMMAND_MODE, command};
WritePacketToEzI2C(buffer, 2);
}
// 发送数据到 SSD1306
void ssd1306_send_data(uint8_t data) {
uint8_t buffer[2] = {SSD1306_DATA_MODE, data};
WritePacketToEzI2C(buffer, 2);
}
// 初始化 SSD1306
void ssd1306_init() {
// 复位 OLED
Cy_SysLib_Delay(1); // 短暂延时
// 发送初始化命令
ssd1306_send_command(0xAE); // 关闭显示
ssd1306_send_command(0xD5); // 设置显示时钟分频比/振荡器频率
ssd1306_send_command(0x80);
ssd1306_send_command(0xA8); // 设置多路复用率
ssd1306_send_command(0x3F);
ssd1306_send_command(0xD3); // 设置显示偏移
ssd1306_send_command(0x00);
ssd1306_send_command(0x40); // 设置显示开始行
ssd1306_send_command(0x8D); // 电荷泵设置
ssd1306_send_command(0x14);
ssd1306_send_command(0x20); // 设置内存寻址模式
ssd1306_send_command(0x00);
ssd1306_send_command(0xA1); // 设置段重映射
ssd1306_send_command(0xC8); // 设置 COM 输出扫描方向
ssd1306_send_command(0xDA); // 设置 COM 引脚硬件配置
ssd1306_send_command(0x12);
ssd1306_send_command(0x81); // 设置对比度控制
ssd1306_send_command(0xCF);
ssd1306_send_command(0xD9); // 设置预充电周期
ssd1306_send_command(0xF1);
ssd1306_send_command(0xDB); // 设置 VCOMH 取消选择级别
ssd1306_send_command(0x40);
ssd1306_send_command(0xA4); // 全局显示开启
ssd1306_send_command(0xA6); // 设置正常显示
ssd1306_send_command(0xAF); // 开启显示
}
// 清屏
void ssd1306_clear_screen(uint8_t mode) {
for (uint8_t page = 0; page < 8; page++) {
ssd1306_send_command(0xB0 | page); // 设置页地址
ssd1306_send_command(0x00); // 设置列低地址
ssd1306_send_command(0x10); // 设置列高地址
for (uint8_t col = 0; col < SSD1306_WIDTH; col++) {
ssd1306_send_data(mode);
}
}
}
程序中,我添加了一个用于清展的函数,传于Mode来实现对整个屏的刷新。
7、在ssd1306_driver.h中把供用户使用的两个函数定义好即可:
/*
* ssd1306_driver.h
*
* Created on: 2025年4月13日
* Author: liujianhua
*/
#ifndef SOURCE_SSD1306_DRIVER_H_
#define SOURCE_SSD1306_DRIVER_H_
void ssd1306_init();
void ssd1306_clear_screen(uint8_t mode);
#endif /* SOURCE_SSD1306_DRIVER_H_ */
8、接下来在Main主函数中添加测试代码,在工程中模版中已有i2c_marster_init了,所以我们只需要添加oled的测试函数即可,整体代码如下:
/* Header file includes */
#include "cy_pdl.h"
#include "cybsp.h"
#include "I2CMaster.h"
#include "I2CSlave.h"
#include "ssd1306_driver.h"
/*******************************************************************************
* Macros
*******************************************************************************/
/* LED Status */
#define OFF CYBSP_LED_STATE_OFF
#define ON CYBSP_LED_STATE_ON
/* Delay duration */
#define CMD_TO_CMD_DELAY (1000UL)
int main(void)
{
cy_rslt_t result;
/* Initialize the device and board peripherals */
result = cybsp_init() ;
if (result != CY_RSLT_SUCCESS)
{
CY_ASSERT(0);
}
status = initMaster();
if(status != I2C_SUCCESS)
{
handle_error();
}
/* Enable interrupts */
__enable_irq();
ssd1306_init(); //初始化oled
for(;;)
{
ssd1306_clear_screen(0); //全屏黑
Cy_SysLib_Delay(CMD_TO_CMD_DELAY);
ssd1306_clear_screen(0xFF); //全屏白
Cy_SysLib_Delay(CMD_TO_CMD_DELAY);
Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN);
}
}
/* [] END OF FILE */
【测试效果】
编译后下载到开发板,按上面的原理图将OLED与开发板连接起来。然后我们就看OLED屏交规刷新了:
【总结】
ModusToolbox提供好了非常好的外设模版,我们在使用他的外设时,非常方便的就能实现即定功能。当然这只是一个初步的测试,还没有将有可能错误发生的进行处理。
|