经过昨天的学习,已经了解和初步学会配置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闪烁