【硬件IIC】stm32单片机利用硬件IIC驱动OLED屏幕

之前操作OLED屏幕都是用GPIO模拟IIC去驱动,最近打算用硬件IIC去驱动,于是写下这个demo,在这个过程中遇到一点小坑,记录一下,本文章非小白教程,所以只突出踩到的坑点,文章中涉及到的OLED也是网上资料写烂的,所以不懂的同学可以万能的百度。话不多说开始。

目标

使用硬件IIC驱动OLED屏,显示英文字符串“I am Rio”
在这里插入图片描述

完整项目工程链接: stm32F103C8T6驱动OLED屏显示字符

同时附上一篇网上找的介绍这个OLED屏的文章
0.96寸OLED(SSD1306)屏幕显示(一)——基础功能介绍

硬件

MCU: stm32f103c8t6

屏幕: 0.96寸OLED(SSD1306)

本次使用的是 gpio 的PB6,PB7脚,这两个脚位可以复用硬件I2C1。

驱动程序

代码中已加入详细注释,放心食用

GPIO和IIC初始化配置

这里遇到第一个坑,就是在配置IO的模式时,一开始设置成推挽输出GPIO_MODE_OUTPUT_PP,因为想着就接一个IIC设备,推挽输出或者开漏输出,都影响不大,结果还说遇到坑了,配置成推挽输出之后,程序会出现卡死现象,debug之后发现程序卡死在函数HAL_I2C_Init(&hi2c1)里; 然后进入到错误HardFault_Handler()中,怀疑是HAL_I2C_Init中的某些设置与设置成推挽输出GPIO_MODE_OUTPUT_PP出现冲突导致硬件错误,具体还未深入研究,欢迎大佬补充。

将GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;后不再出现卡死现象。

I2C_HandleTypeDef hi2c1;
uint16_t slaveAddr = 0x78;	//OLED显示屏的IIC设备地址,改地址为写地址
void oled_gpio_init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOB_CLK_ENABLE();	//使能GPIOB口时钟/**I2C1 GPIO ConfigurationPB6     ------> I2C1_SCLPB7     ------> I2C1_SDA*/GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;//当时就是这句导致程序卡死,用HAL库实现硬件IIC时还是乖乖用开漏模式好
//    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;		//配置为开漏模式GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* I2C1 clock enable */__HAL_RCC_I2C1_CLK_ENABLE();hi2c1.Instance = I2C1;hi2c1.Init.ClockSpeed = 100000; // 设置I2C时钟速度为100kHz(可以根据需要调整)hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比(通常不需要修改)hi2c1.Init.OwnAddress1 = 0; // 主设备通常不需要设置自己的地址hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7位地址模式hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 双地址模式禁用hi2c1.Init.OwnAddress2 = 0; // 不使用第二个地址hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 通用调用模式禁用hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 时钟延伸模式禁用HAL_I2C_Init(&hi2c1);}
IIC的读和写

这里由于没有理解透HAL库的函数踩到第二个坑,用的这一款OLED在写入命令或者数据时,时序是与从机建立IIC通讯开始信号后,就连续写入control byte + data byte; 比如写数据为 0x40 + data; 写命令是0x00 + cmd;

而HAL库提供的IIC读写函数有好几种,如:

HAL_I2C_Master_Receive(&hi2c1, DevAddress, pData, Size, Timeout);
HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);

这一组是用来作为主机时,对从机进行读写操作,而我一开始在往从机写数据的时候,使用的就是HAL_I2C_Master_Transmit函数,将control byte + data byte;分开两次来发送,结果就写失败了。原因是分开两次写的话,HAL_I2C_Master_Transmit每次写完一个byte数据之后,就结束该次通讯,这就相当于没有发送完整的control byte + data byte时序;而是

第一次发送control byte结束;第二次发送data byte结束;所以两次没有一次是完整的组合时序。正确的是应该在一次通讯中发送两个byte数据才行;

所以我将要发送的时序进行组合,然后一次发送两个数据即可

uint8_t dataArr[2] = {0x40, data};
I2C_SendData(slaveAddr, dataArr, 2, 1000);

//对HAL_I2C_Master_Transmit函数进行封装
HAL_StatusTypeDef I2C_SendData(uint16_t DevAddress, uint8_t* pData, uint16_t Size, uint32_t Timeout)
{return HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);
}//对HAL_I2C_Master_Receive函数进行封装
HAL_StatusTypeDef I2C_ReceiveData(uint16_t DevAddress, uint8_t* pData, uint16_t Size, uint32_t Timeout)
{return HAL_I2C_Master_Receive(&hi2c1, DevAddress, pData, Size, Timeout);
}//封装函数,实现对往OLED中写入一个字节数据
void oled_write_data(uint8_t data)
{    /*错误的一次只写一个数据,时序不完整*/
//    uint8_t dataOptionByte = 0x00;   
//    I2C_SendData(slaveAddr, &dataOptionByte, 1, 1000);
//    I2C_SendData(slaveAddr, &dataData, 1, 1000);/*一次只写入完整的时序即可*/uint8_t dataArr[2] = {0x40, data};I2C_SendData(slaveAddr, dataArr, 2, 1000);/*使用HAL_I2C_Mem_Write函数,也可实现相同效果*/
//    uint8_t tmpData = data;
//    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
//										&tmpData, 1, 0xff);
}//封装函数,实现对往OLED中写入一个字节命令
void oled_write_cmd(uint8_t cmd)
{/*错误的一次只写一个数据,时序不完整*/
//    uint8_t cmdOptionByte = 0x00;
//    I2C_SendData(slaveAddr, &cmdOptionByte, 1, 1000);
//    I2C_SendData(slaveAddr, &dataCmd, 1, 1000);/*一次只写入完整的时序即可*/uint8_t cmdArr[2] = {0x00, cmd};I2C_SendData(slaveAddr, cmdArr, 2, 1000);/*使用HAL_I2C_Mem_Write函数,也可实现相同效果*/
//    uint8_t tmpCmd = cmd;
//    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
//										&tmpCmd, 1, 0xff);}

完整代码

oled.c

#include "oled.h"
#include "delay.h"
#include "font.h"I2C_HandleTypeDef hi2c1;
uint16_t slaveAddr = 0x78;
void oled_gpio_init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOB_CLK_ENABLE();/**I2C1 GPIO ConfigurationPB6     ------> I2C1_SCLPB7     ------> I2C1_SDA*/GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;//    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  //GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* I2C1 clock enable */__HAL_RCC_I2C1_CLK_ENABLE();hi2c1.Instance = I2C1;hi2c1.Init.ClockSpeed = 100000; // 设置I2C时钟速度为100kHz(可以根据需要调整)hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比(通常不需要修改)hi2c1.Init.OwnAddress1 = 0; // 主设备通常不需要设置自己的地址hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7位地址模式hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 双地址模式禁用hi2c1.Init.OwnAddress2 = 0; // 不使用第二个地址hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 通用调用模式禁用hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 时钟延伸模式禁用HAL_I2C_Init(&hi2c1);}
/*oled_gpio_init()函数中不配置GPIO口和使能时钟,在MSP函数中配置也可以,因为
执行HAL_I2C_Init(&hi2c1);时,会执行HAL_I2C_MspInit函数
*/
//void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
//{//  GPIO_InitTypeDef GPIO_InitStruct = {0};
//  if(i2cHandle->Instance==I2C1)
//  {
//  /* USER CODE BEGIN I2C1_MspInit 0 *///  /* USER CODE END I2C1_MspInit 0 *///    __HAL_RCC_GPIOB_CLK_ENABLE();
//    /**I2C1 GPIO Configuration
//    PB6     ------> I2C1_SCL
//    PB7     ------> I2C1_SDA
//    */
//    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
//    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
//    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
//    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);//    /* I2C1 clock enable */
//    __HAL_RCC_I2C1_CLK_ENABLE();
//  /* USER CODE BEGIN I2C1_MspInit 1 *///  /* USER CODE END I2C1_MspInit 1 */
//  }
//}HAL_StatusTypeDef I2C_SendData(uint16_t DevAddress, uint8_t* pData, uint16_t Size, uint32_t Timeout)
{return HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);
}HAL_StatusTypeDef I2C_ReceiveData(uint16_t DevAddress, uint8_t* pData, uint16_t Size, uint32_t Timeout)
{return HAL_I2C_Master_Receive(&hi2c1, DevAddress, pData, Size, Timeout);
}void oled_write_data(uint8_t data)
{    
//    uint8_t dataOptionByte = 0x00;   
//    I2C_SendData(slaveAddr, &dataOptionByte, 1, 1000);
//    I2C_SendData(slaveAddr, &dataData, 1, 1000);uint8_t dataArr[2] = {0x40, data};I2C_SendData(slaveAddr, dataArr, 2, 1000);
//    uint8_t tmpData = data;
//    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
//										&tmpData, 1, 0xff);
}void oled_write_cmd(uint8_t cmd)
{
//    uint8_t cmdOptionByte = 0x00;
//    I2C_SendData(slaveAddr, &cmdOptionByte, 1, 1000);
//    I2C_SendData(slaveAddr, &dataCmd, 1, 1000);uint8_t cmdArr[2] = {0x00, cmd};I2C_SendData(slaveAddr, cmdArr, 2, 1000);//    uint8_t tmpCmd = cmd;
//    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
//										&tmpCmd, 1, 0xff);}void oled_init(void)
{oled_gpio_init();delay_ms(100);oled_write_cmd(0xAE);    //设置显示开启/关闭,0xAE关闭,0xAF开启oled_write_cmd(0xD5);    //设置显示时钟分频比/振荡器频率oled_write_cmd(0x80);    //0x00~0xFFoled_write_cmd(0xA8);    //设置多路复用率oled_write_cmd(0x3F);    //0x0E~0x3Foled_write_cmd(0xD3);    //设置显示偏移oled_write_cmd(0x00);    //0x00~0x7Foled_write_cmd(0x40);    //设置显示开始行,0x40~0x7Foled_write_cmd(0xA1);    //设置左右方向,0xA1正常,0xA0左右反置oled_write_cmd(0xC8);    //设置上下方向,0xC8正常,0xC0上下反置oled_write_cmd(0xDA);    //设置COM引脚硬件配置oled_write_cmd(0x12);oled_write_cmd(0x81);    //设置对比度oled_write_cmd(0xCF);    //0x00~0xFFoled_write_cmd(0xD9);    //设置预充电周期oled_write_cmd(0xF1);oled_write_cmd(0xDB);    //设置VCOMH取消选择级别oled_write_cmd(0x30);oled_write_cmd(0xA4);    //设置整个显示打开/关闭oled_write_cmd(0xA6);    //设置正常/反色显示,0xA6正常,0xA7反色oled_write_cmd(0x8D);    //设置充电泵oled_write_cmd(0x14);oled_write_cmd(0xAF);    //开启显示}
// y的值为page,屏幕总共有8个page
void oled_set_cursor(uint8_t x, uint8_t y)
{oled_write_cmd(0xB0 + y);   //确定在哪一个page, 第一个page地址是B0oled_write_cmd((x & 0x0F) | 0x00);  //取字节的低位oled_write_cmd(((x & 0xF0) >> 4) | 0x10);   //取字节的高位,|0x10是因为oled芯片的要求}//清屏函数,每次刷新画面时,需要清屏,防止上一帧数据残留
void oled_fill(uint8_t data)
{uint8_t i, j;for( i = 0; i <8 ; i++){oled_set_cursor(0, i);for(j = 0; j < 128; j++){   //page模式,地址每次都自动偏移oled_write_data(data);}}}//输入字符的坐标、ASCII码、大小; size一般是宽为高的1/2;
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)
{uint8_t i, j, page;num = num - ' ';page = size/8;if (size % 8)page++;for(j = 0; j < page; j++ ){oled_set_cursor(x, y + j);for(i = size/2 * j; i < size/2 *(j + 1); i++)   //即每一个page写size/2宽的数据,{if (size == 12)oled_write_data(ascii_6X12[num][i]);else if (size == 16)oled_write_data(ascii_8X16[num][i]);else if (size == 24)oled_write_data(ascii_12X24[num][i]);}}
}//输入字符的坐标、字符指针、大小; size一般是宽为高的1/2;
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size)
{while( *p !='\0'){oled_show_char(x, y, *p, size);x += size/2;p++;}
}

oled.h

#ifndef __OLED_H__
#define __OLED_H__#include "sys.h"void oled_gpio_init(void);
void oled_init(void);
void oled_write_cmd(uint8_t cmd);
void oled_write_data(uint8_t data);void oled_set_cursor(uint8_t x, uint8_t y);
void oled_fill(uint8_t data);
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size);
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size);
#endif

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "oled.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* 初始化LED灯 */uart1_init(115200);printf("hello world!\r\n");oled_init();oled_fill(0x00);oled_set_cursor(0, 0);//划线
//    oled_write_data(0x80);
//    oled_write_data(0x80);
//    oled_write_data(0x80);
//    oled_write_data(0x80);
//    oled_write_data(0x80);
//    oled_write_data(0x80);//    oled_show_char(0, 0, 'x', 24);oled_show_string(0, 2, "I am Rio", 24);while(1){ led1_on();led2_off();delay_ms(500);led1_off();led2_on();delay_ms(500);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/63440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python如何自动加空格

首先&#xff0c;需要进行打开的一个pycharm的软件&#xff0c;可进行双击的打开该软件。 可以看到的是在当前的打开的文件中&#xff0c;格式相对较乱一下。格式不对会格式错误。 然后点击菜单栏中的“code”。 在弹出的下拉菜单中选择“reformat code”选项。 可以看到的是在…

【开源免费】基于SpringBoot+Vue.JS网上订餐系统(JAVA毕业设计)

本文项目编号 T 018 &#xff0c;文末自助获取源码 \color{red}{T018&#xff0c;文末自助获取源码} T018&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

串口通信和SPI通信详解

0、背景 在现代嵌入式系统中&#xff0c;通信是不同模块之间交换数据的核心。串口通信和 SPI&#xff08;串行外设接口&#xff09;是两种常见的通信方式&#xff0c;它们各自有独特的优势和适用场景。 1、串口通信 1.1、串口通信概念 串口通信是一种常见的异步串行通信协议…

javase-17、API.数学相关

一、Math类 Math类提供了大量的静态方法来便于我们实现数学计算&#xff0c;如求绝对值、取最大或最小值等。 https://doc.qzxdp.cn/jdk/17/zh/api/java.base/java/lang/Math.html 所在模块&#xff1a;java.base所在包&#xff1a; java.lang static double abs(double a)…

答题考试系统v1.6.1高级版源码分享+uniapp+搭建测试环境

一.系统介绍 一款基于FastAdminThinkPHPUniapp开发的小程序答题考试系统&#xff0c;支持多种试题类型、多种试题难度、练题、考试、补考模式&#xff0c;提供全部前后台无加密源代码&#xff0c;支持私有化部署 二.测试环境 系统环境&#xff1a;CentOS、 运行环境&#x…

浅谈仓颉语言的优劣

仓颉语言&#xff0c;作为华为自研的新一代编程语言&#xff0c;以其高效、安全、现代化的特点&#xff0c;引起了广泛的关注。 仓颉语言的优势 高效并发 仓颉语言的一大亮点是其轻松并发的能力。它实现了轻量化用户态线程和并发对象库&#xff0c;使得高效并发变得轻松。仓颉…

Sass系统数据隔离的三种方式

1.完全独立的数据库 为每一个租户单独部署一个数据库 优点&#xff1a;达到了真正的按租户进行隔离&#xff0c;不同租户之间相互没有影响&#xff0c;可以针对一些特殊租户例如大客户做一些定制化的开发&#xff0c;计费相对简单&#xff0c;按照资源使用进行计费。 缺点&…

FFmpeg 主要结构体剖析

FFmpeg 探索之旅 FFmpeg 主要结构体剖析 FFmpeg 探索之旅前言1、AVFormatContext2、AVCodecContext3、AVCodec4、AVStream5、AVPacket6、AVFrame7、AVCodecParameters7、SwsContext8、AVIOContext9、AVRational 总结 前言 嘿&#xff0c;各位小伙伴&#xff01;在如今这个多媒…

经典电荷泵/Charge pump——1998.JSSC

电路结构 工作原理 M3 and M4 are the series switches, and M5, M6 switch to the highest voltage. If M5 and M6 are missing, having a large capacitor is of absolute necessity, because must always stay between 2 Vin and 2Vin - Uj to avoid switching on the vert…

Swin transformer 论文阅读记录 代码分析

该篇文章&#xff0c;是我解析 Swin transformer 论文原理&#xff08;结合pytorch版本代码&#xff09;所记&#xff0c;图片来源于源paper或其他相应博客。 代码也非原始代码&#xff0c;而是从代码里摘出来的片段&#xff0c;配上简单数据&#xff0c;以便理解。 当然&…

GPT-Omni 与 Mini-Omni2:创新与性能的结合

近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;各种模型和平台应运而生&#xff0c;以满足从个人用户到企业级应用的多样化需求。在这一领域&#xff0c;GPT-Omni 和 Mini-Omni2 是两款备受瞩目的技术产品&#xff0c;它们凭借独特的设计和强大的功能&#xff0c;在…

龙迅#LT7911E适用于EDP/DP/TPYE-C转MIPIDSI应用,支持图像处理功能,内置I2C,主应用副屏显示,投屏领域!

1. 描述 LT7911E 是一款高性能 eDP 转 MIPI D-PHY 转换器&#xff0c;旨在将 eDP 源连接到 MIPI 显示面板。 LT7911E 集成了一个符合 eDP1.4 标准的接收器&#xff0c;支持 1.62Gbps 至 5.67Gbps 的输入数据&#xff0c;以 270Mbps 的递增步长&#xff0c;以及一个 2 端口 D…

C语言——实现求出最大值

问题描述&#xff1a;利用C语言自定义函数求出一维数组里边最大的数字 //利用函数找最大数#include<stdio.h>int search(int s[9]) //查找函数 {int i , max s[0] , max_xia 0;for(i0;i<9;i){if(s[i] > max){max_xia i;max s[max_xia];}}return max; } in…

解锁 draw.io 流程图制作工具Docker私有化部署(2/2)

一、draw.io 流程图制作工具简介 &#xff08;一&#xff09;基础介绍 draw.io 是一款备受青睐的开源流程图软件&#xff0c;它有着诸多优点。首先&#xff0c;其界面十分整洁有序&#xff0c;完全没有广告的干扰&#xff0c;并且所有功能都是免费向用户开放的&#xff0c;这一…

[HNCTF 2022 Week1]baby_rsa

源代码&#xff1a; from Crypto.Util.number import bytes_to_long, getPrime from gmpy2 import * from secret import flag m bytes_to_long(flag) p getPrime(128) q getPrime(128) n p * q e 65537 c pow(m,e,n) print(n,c) # 62193160459999883112594854240161159…

docker run命令大全

docker run命令大全 基本语法常用选项基础选项资源限制网络配置存储卷和挂载环境变量重启策略其他高级选项示例总结docker run 命令是 Docker 中最常用和强大的命令之一,用于创建并启动一个新的容器。该命令支持多种选项和参数,可以满足各种使用场景的需求。以下是 docker ru…

Java中JDBC过时方法的替代方案以及JDBC为什么过时详细分析

在Java中&#xff0c;JDBC的一些方法因为安全问题、性能问题或者因为引入了更好的替代方法已经被标记为过时&#xff08;Deprecated&#xff09;。 以下是一些被过时的JDBC方法以及它们的替代方案&#xff1a; 1.DriverManager.getDrivers(): 这个方法用于获取所有当前注册的J…

详细指南:在Ubuntu 20.04 ROS 1环境下设置和使用OpenNI2 SDK

详细指南&#xff1a;在Ubuntu 20.04 ROS 1环境下设置和使用OpenNI2 SDK 要在Ubuntu 20.04系统上使用ROS 1环境中的OpenNI2 SDK&#xff0c;您需要按照一系列有组织的步骤进行操作&#xff0c;以确保软件和驱动正确安装&#xff0c;并配置好相应的开发环境。以下是详细的步骤说…

RK3568平台(Kbuild篇)vmlinux 编译过程

一.vmlinux是什么 vmlinux 是 Linux 操作系统的内核映像文件,它包含了 Linux 内核的所有功能代码和必要的数据结构。这个文件通常是没有经过压缩和符号表去除的原始可执行文件。 具体来说,vmlinux 文件是编译后的 Linux 内核的最终产物,通常是 ELF(可执行和可链接格式)格…

Flink2.0未来趋势中需要注意的一些问题

手机打字&#xff0c;篇幅不长&#xff0c;主要讲一下FFA中关于Flink2.0的未来趋势&#xff0c;直接看重点。 Flink Forward Asia 2024主会场有一场关于Flink2.0的演讲&#xff0c;很精彩&#xff0c;官方也发布了一些关于Flink2.0的展望和要解决的问题。 1.0时代和2.0时代避免…