前言
在前面一章中, 学习了 串口通信以及定时器, 本章节中将介绍I2C通信,使用 I2C 通信方式点亮 OLED 模块。由于 OLED 模块支持多种通信方式, OLED 模块的 I2C 通信过程主要通过在数据层进行二次打包, 以达到分类数据包的目的, 以便适配 OLED 的多种通信方式。
准备工具
软件:STM32CubeMx、Keil5 MDK
硬件:STM32F103C8T6核心板、下载器ST_LINK、OLEDI2C通信4针屏幕
本章节工程已上传至百度网盘,此链接永久有效
链接:https://pan.baidu.com/s/13UiC3hnn84yIsA5N1nrmmg?pwd=w8cx
提取码:w8cx
I2C通信
I2C 是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。两线制代表 I2C 只需两根信号线,一根数据线 SDA,另一根是时钟线 SCL。I2C 总线允许挂载多个主设备, 但总线时钟同一时刻只能由一个主设备产生,并且要求每个连接到总线上的器件都有唯一的 I2C 地址,从设备可以被主设备寻址。I2C 通信具有几类信号:
开始信号 S: 当 SCL 处于高电平时, SDA 从高电平拉低至低电平,代表数据传输的开始。
结束信号 P: 当 SCL 处于高电平时, SDA 从低电平拉高至高电平,代表数据传输结束。
数据信号: 数据信号每次都传输 8 位数据,每一位数据都在一个时钟周期内传递,当SCL 处于高电平时候, SDA 数据线上的电平需要稳定,当 SCL 处于低电平的时候,SDA 数据线上的电平允许改变。
应答信号 ACK/NACK: 应答信号是主机发送 8bit 数据,从机对主机发送低电平,表示已经接受数据。
常见用于读取传感器数据的 I2C 传输过程如下表所示:
整个 I2C 通信过程理解成收发快递的过程,设备 I2C 地址理解成学校快递柜的地址,读写位代表寄出和签收快递,寄存器地址则是快递柜上的箱号, 而数据便是需要寄出或者签收的快递。整个过程便是如同到学校的快递柜(从机 I2C 地址),对第几号柜箱(寄存器地址 ,进行寄出或者签收快递(数据) 的过程。
IIC在CubeMx中的配置
1. 在 connectivity 下找到 I2C1;
2. 配置成 I2C;
3. 配置成 Fast Mode;
4. 其他保持默认。
生成代码,打开工程。
添加以下代码到工程中
/**
* @brief 写数据或者指令到OLED, 如果使用的是SPI,请重写这个函数
* @param[in] dat: 要写入的字节
* @param[in] cmd: OLED_CMD 代表写入的字节是指令; OLED_DATA 代表写入的字节是数据
* @retval none
*/
void oled_write_byte(uint8_t dat, uint8_t cmd)
{
static uint8_t cmd_data[2];
if(cmd == OLED_CMD)
{
cmd_data[0] = 0x00;
}
else
{
cmd_data[0] = 0x40;
}
cmd_data[1] = dat;
HAL_I2C_Master_Transmit(&hi2c1, OLED_I2C_ADDRESS, cmd_data, 2, 10);
}
根据 OLED 的通信方式,需要在第一个字节中指明之后的数据类型,如果是控制指令则需要发送 0x00, 如果是数据指令则需要发送 0x40。
/**
* @brief 初始化OLED模块,
* @param[in] none
* @retval none
*/
void OLED_init(void)
{
oled_write_byte(0xAE, OLED_CMD); //display off
oled_write_byte(0x20, OLED_CMD); //Set Memory Addressing Mode
oled_write_byte(0x10, OLED_CMD); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
oled_write_byte(0xb0, OLED_CMD); //Set Page Start Address for Page Addressing Mode,0-7
oled_write_byte(0xc8, OLED_CMD); //Set COM Output Scan Direction
oled_write_byte(0x00, OLED_CMD); //---set low column address
oled_write_byte(0x10, OLED_CMD); //---set high column address
oled_write_byte(0x40, OLED_CMD); //--set start line address
oled_write_byte(0x81, OLED_CMD); //--set contrast control register
oled_write_byte(0xff, OLED_CMD); //brightness 0x00~0xff
oled_write_byte(0xa1, OLED_CMD); //--set segment re-map 0 to 127
oled_write_byte(0xa6, OLED_CMD); //--set normal display
oled_write_byte(0xa8, OLED_CMD); //--set multiplex ratio(1 to 64)
oled_write_byte(0x3F, OLED_CMD); //
oled_write_byte(0xa4, OLED_CMD); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
oled_write_byte(0xd3, OLED_CMD); //-set display offset
oled_write_byte(0x00, OLED_CMD); //-not offset
oled_write_byte(0xd5, OLED_CMD); //--set display clock divide ratio/oscillator frequency
oled_write_byte(0xf0, OLED_CMD); //--set divide ratio
oled_write_byte(0xd9, OLED_CMD); //--set pre-charge period
oled_write_byte(0x22, OLED_CMD); //
oled_write_byte(0xda, OLED_CMD); //--set com pins hardware configuration
oled_write_byte(0x12, OLED_CMD);
oled_write_byte(0xdb, OLED_CMD); //--set vcomh
oled_write_byte(0x20, OLED_CMD); //0x20,0.77xVcc
oled_write_byte(0x8d, OLED_CMD); //--set DC-DC enable
oled_write_byte(0x14, OLED_CMD); //
oled_write_byte(0xaf, OLED_CMD); //--turn on oled panel
}
OLED_init 函数,该函数主要配置 OLED 参数, 通过调用 oled_write_byte 传入OLED_CMD, 传输控制指令完成配置 。
/**
* @brief 操作GRAM内存(128*8char数组)
* @param[in] pen: 操作类型.
PEN_CLEAR: 设置为0x00
PEN_WRITE: 设置为0xff
PEN_INVERSION: 按位取反
* @retval none
*/
void OLED_operate_gram(pen_typedef pen)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
for (n = 0; n < 128; n++)
{
if (pen == PEN_WRITE)
{
OLED_GRAM[n][i] = 0xff;
}
else if (pen == PEN_CLEAR)
{
OLED_GRAM[n][i] = 0x00;
}
else
{
OLED_GRAM[n][i] = 0xff - OLED_GRAM[n][i];
}
}
}
}
/**
* @brief 发送数据到OLED的GRAM
* @param[in] none
* @retval none
*/
void OLED_refresh_gram(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
OLED_set_pos(0, i);
for (n = 0; n < 128; n++)
{
oled_write_byte(OLED_GRAM[n][i], OLED_DATA);
}
}
}
/**
* @brief 显示一个字符
* @param[in] row: 字符的开始行
* @param[in] col: 字符的开始列
* @param[in] chr: 字符
* @retval none
*/
void OLED_show_char(uint8_t row, uint8_t col, uint8_t chr)
{
uint8_t x = col * 6;
uint8_t y = row * 12;
uint8_t temp, t, t1;
uint8_t y0 = y;
chr = chr - ' ';
for (t = 0; t < 12; t++)
{
temp = asc2_1206[chr][t];
for (t1 = 0; t1 < 8; t1++)
{
if (temp&0x80)
OLED_draw_point(x, y, PEN_WRITE);
else
OLED_draw_point(x, y, PEN_CLEAR);
temp <<= 1;
y++;
if ((y - y0) == 12)
{
y = y0;
x++;
break;
}
}
}
}
/**
* @brief 显示一个字符串
* @param[in] row: 字符串的开始行
* @param[in] col: 字符串的开始列
* @param[in] chr: 字符串
* @retval none
*/
void OLED_show_string(uint8_t row, uint8_t col, uint8_t *chr)
{
uint8_t n =0;
while (chr[n] != '\0')
{
OLED_show_char(row, col, chr[n]);
col++;
if (col > 20)
{
col = 0;
row += 1;
}
n++;
}
}
显示过程如下:
1. OLED 模块的初始化,调用 OLED_init
2. 通过显示字符函数,对 stm32 内的 GRAM 数组进行操作
3. 调用 OLED_refresh_gram 函数将 GRAM 数据传输到 OLED 模块的 GRAM 进行显示。
主函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM4_Init();
MX_I2C1_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1); //初始化BUZZER
HAL_TIM_Base_Start_IT(&htim3); //开启定时器3中断
OLED_init(); //OLED初始化
// Solitary_brave();
OLED_operate_gram(PEN_CLEAR);
OLED_show_string(0,6,"LRJ_ROBOT");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(tim3_delay.count_10ms != tim3_delay.last_count_10ms)
{
GPIO_PinState pin_state = HAL_GPIO_ReadPin(LEDD_GPIO_Port, LEDD_Pin);//获取LED电平
OLED_printf(2,5,"LED_PIN = %d",pin_state);
tim3_delay.last_count_10ms = tim3_delay.count_10ms;
}
if(tim3_delay.count_400ms != tim3_delay.last_count_400ms)
{
OLED_refresh_gram();//屏幕刷新 400ms
HAL_GPIO_TogglePin(LEDD_GPIO_Port, LEDD_Pin);
tim3_delay.last_count_400ms = tim3_delay.count_400ms;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
OLED屏幕实时显示LED电平变化