【蓝桥杯物联网赛项学习日志】Day3 关于IIC

本文介绍了STM32配置IIC接口连接OLED显示屏的过程,包括IIC的基本概念、STM32的GPIO配置、CubeMax中配置I2C3、I2C库函数的使用,以及OLED显示驱动的软件设计。通过实例展示了如何初始化I2C,发送和接收数据,并实现OLED显示时间的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经过昨天的学习,已经了解和初步学会配置CubeMax进行初始化配置。今天就开始下一章节的学习,关于IIC。

关键词:I2C OLED SSD1306

理论基础

串行通信接口通讯方式分,可以分为两种,分别是同步和异步。按照数据的传输方向又可分为单工、半双工和全双工通信。
同步:通讯双方靠一根时钟线约定通讯速率
异步:通讯双方可以使用各自的通讯时钟,约定通讯速率进行通信
单工:单方向通信,只能收数据或者只能发数据
半双工:可以分时发收数据
全双工:可以同时发收数据,单独时钟线


IIC 简介


IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接
微控制器及其外围设备。它是由数据线 SDA 时钟 SCL 构成的串行总线,可发送和接收数据。
在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答
信号。
传输类型同步、半双工,带数据应答
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接
收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为
受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

 GPIO引脚配置

查询文档可知OLED显示屏使用的是PA8,PB4,PB5 三个引脚

 在STM32L071上有两个硬件I2C接口,记作I2C1 I2C3,全都支持I2C标准模式和I2C快速模式。

 在CubeMax中配置iic3

1.单击I2C3
2.选择I2C
3.配置引脚模式,单击GPIO Settings(默认配置也可以)

PA8:    I2C3_SCL,复用开漏模式,上拉,超高速
PB4:    I2C3_SDA,复用开漏模式,上拉,超高速

4. 注意配置初始化PB5引脚,这是OLED的电源控制引脚。

配置完成后生成初始化程序(时钟配置,生成代码请参考上一篇笔记)

I2C库函数

初始化函数:

HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c);

hi2c:  指的是I2C句柄; 初始化I2C的名称,定时值,自身地址1,地址模式(7位)或(10位)结构体参数。

主设备发送:

I2C数据传输此处采用的是阻塞式数据传输。阻塞式数据传输使用方便,且I2C接口传输速率不高,一般传输数据量也不大,阻塞式传输是常用的数据传输模式。

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

DevAddress:器件地址
pData:发送的数据指针
Size:发送的数据数量
Timeout:超时(ms)

主设备接收函数:

HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size, uint32_t Timeout);

OLED软件设计与实现:

物联网竞赛平台使用的是128*32 OLED显示屏,I2C接口,控制器是SSD1306。
竞赛会给OLED驱动程序的的资源包,在此基础上进行修改完成OLED软件设计部分。

底层函数

1.在i2c.h中添加以下函数声明

/* USER CODE BEGIN Prototypes */
void OLED_Write(uint8_t ucType,uint8_t ucData);
/* USER CODE END Prototypes */

2.在i2c.c中MX_I2C3_Init()后面部分添加延时

  /* USER CODE BEGIN I2C3_Init 2 */
  HAL_Delay(100);					/* 等待OLED电源稳定 */
  /* USER CODE END I2C3_Init 2 */

3.在i2c.c中添OLED_Write();用来写入OLED数据

/* USER CODE BEGIN 1 */
void OLED_Write(uint8_t ucType,uint8_t ucData)
{
	uint8_t pData[2];
	
	pData[0] = ucType;
	pData[1] = ucData;
	HAL_I2C_Master_Transmit(&hi2c3,0x78,pData,2,10);
}

/* USER CODE END 1 */

高层函数

添加头文件 oled.h

#ifndef __OLED_H
#define __OLED_H

#include "main.h"
#include "i2c.h"


#define Max_Column 128
#define Max_Row 32

typedef enum
{
		TYPE_COMMAND = 0,
		TYPE_DATA = 0x40
}OLED_TYPE;


void OLED_Init(void);	
void OLED_Clear(void);
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size);
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t size);
#endif

添加.C文件

#include "stm32l0xx.h"                  // Device header
#include "oled.h"
#include "font.h"
#include "i2c.h"
/**
  * @brief  OLED设置位置
  * @param  x横坐标,y纵坐标
  * @retval 无
  */
void OLED_SetPos(uint8_t x,uint8_t y)
{
	OLED_Write(TYPE_COMMAND,x & 0x0f);
	OLED_Write(TYPE_COMMAND,0x10 +((x &0xf0) >> 4));
	OLED_Write(TYPE_COMMAND,0xb0 + y);
}
/* 清除屏幕 */
void OLED_Clear(void)
{
  uint8_t i, j;

  for(i = 0; i < 4; i++)
  {
    OLED_SetPos(0, i);
    for(j = 0; j < 128; j++)
      OLED_Write(TYPE_DATA, 0);
  }
}
/**
  * @brief OLED屏幕初始化。
  * @param None
  * @retval None
  */
void OLED_Init(void)
{
  OLED_Write(TYPE_COMMAND, 0xA1);	/* 段重映射, b0:0,0->0(复位值);1,0->127 */

  OLED_Write(TYPE_COMMAND, 0xA8);	/* 复用比 */
	OLED_Write(TYPE_COMMAND, 0x1F);	/* 0F~3F,16MUX~64MUX(复位值) */

  OLED_Write(TYPE_COMMAND, 0xC8);	/* COM扫描方向, b3:0,正常模式(复位值);b3:1,反转模式 */

  OLED_Write(TYPE_COMMAND, 0xDA);	/* COM引脚配置 */
  OLED_Write(TYPE_COMMAND, 0x00);	/* D4:0,顺序;1,交替(复位值) */

	OLED_Write(TYPE_COMMAND, 0x8D);	/* 充电泵 */
  OLED_Write(TYPE_COMMAND, 0x14);	/* D2:0,禁止(复位值);1,允许 */

  OLED_Clear();										/* 清除屏幕 */
  OLED_Write(TYPE_COMMAND, 0xAF);	/* 开启显示 */
}

/**
  * @brief OLED屏幕显示一个字符。
  * @param x 横向选择位置;
					 y 纵向选择位置;
					 chr 需要显示的字符;
					 size 需要显示的字符大小;
					 @arg 16,选择8*16点阵大小字符。其他均选择6*8点阵字符
  * @retval None
  */
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t size)
{
    uint8_t  c, i;

  	c = chr - ' ';
    if(x > Max_Column - 1)
    {
        x = 0;
        y = y + 2;
    }
    if(size == 16)
    {
        OLED_SetPos(x, y);
        for(i = 0; i < 8; i++)
            OLED_Write(TYPE_DATA, g_F8X16[c * 16 + i]);
        OLED_SetPos(x, y + 1);
        for(i = 0; i < 8; i++)
            OLED_Write(TYPE_DATA, g_F8X16[c * 16 + i + 8]);
    }
    else
    {
        OLED_SetPos(x, y);
        for(i = 0; i < 6; i++)
        {
            OLED_Write(TYPE_DATA, g_F6x8[c][i]);
        }
    }
}

/**
  * @brief OLED屏幕显示一个字符串。
  * @param x 横向选择位置;0
					 y 纵向选择位置;
					 chr 需要显示的字符串;
					 size 需要显示的字符大小;

  * @retval None
  */
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t size)
{
    uint8_t j = 0;
	
    while(chr[j] != '\0')
    {
        OLED_ShowChar(x, y, chr[j], size);
        x += 8;
        if(x > 120)
        {
            x = 0;
            y += 2;
        }
				j++;
    }
}

主函数

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_I2C3_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	OLED_Init();
	sprintf((char*)ucBufs, "HELLO!");
	OLED_ShowString(0,0,ucBufs,16);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		HAL_Delay(500);
		HAL_GPIO_TogglePin(LD5);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

实验现象

 写一个简单的时钟

在主函数中添加三个时间变量

uint8_t ucSec,ucMin,ucHour;                 	/* 秒计时 分计时 小时 */

在系统定时器,void SysTick_Handler(void)中添加如下代码

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  usDly++;
  if(++usTms == 1000)     			/* 1s到 */
  {
    usTms = 0;
    ucSec++;                 		/* 秒加1 */
  }
	if(ucSec == 60)
	{
		ucSec = 0;
		ucMin++;
	}
	if(ucMin == 60)
	{
		ucMin = 0;
		ucHour ++;
        if(ucHour==24)
            ucHour=0;
	}
	
  /* USER CODE END SysTick_IRQn 1 */
}

在主函数中添加以下代码

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_I2C3_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
    OLED_Init();

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		HAL_Delay(500);
		HAL_GPIO_TogglePin(LD5);
	    sprintf((char*)ucBuf, "TIME: %02u:%02u:%02u", ucHour, ucMin, ucSec);
        OLED_ShowString(0, 0, ucBuf, 16);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

实验现象

OLED显示时间,LD5闪烁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值