RFID防伪设计(物联网工程课程设计)DAY4---LCD屏幕显示

重点来了

这个课程设计中硬件方面一共会有两个重点
其中一个自然就是今天要做的OLCD屏幕的驱动
第二个是RFID标签的读写

由于我买的是野火的I2C OLCD屏幕,自然选择野火自带的例程进行修改,让其能够适配HAL库的开发

当然既然是I2C的OLCD,那必然离不开I2C协议
值得一提的是,在stm32要实现I2C,可以选择两种方式
1.硬件I2C
2.软件模拟I2C

虽然火哥在标准库的视频中说过硬件I2C可能会存在一定的问题,但是既然买了板子,当然要用哇,不用岂不是暴殄天物。
所以,我们还是在cubemx中对硬件进行配置,然后驱动OLCD屏幕

I2C是一种协议,当然其核心还是时序逻辑,软件I2C同样好用,但是对编程稍微较高,未来可能会在b站上录制相应的视频

stm32cubemx

首先还是查找硬件相关的手册
这里就不在截图了
总之,选择PB6、PB7引脚输出I2C,正好对应为I2C1的配置

在cubemx中进行配置
RFID防伪设计(物联网工程课程设计)DAY4---LCD屏幕显示
I2C的模式当然就是选择I2C
subus可以自己看看,按照火哥讲的,这个是另一种协议不在考虑范围内
下面就是I2C的详细配置
选择高速模式,这个是由外设决定
OLCD支持高速模式,所以可以这样设置
占空比选择2
延迟Disabled
地址位数7
Disabled
slave地址0(不选用主从模式)

然后生成代码

keil5

好!
工程变得越来越复杂了,我们细心的构建一下工程目录
除了系统自动生成的文件夹外,我们在自己创建一个
user
文件夹,用于存放自己编写的文件
工程目录如下:
RFID防伪设计(物联网工程课程设计)DAY4---LCD屏幕显示
其中**.ioc**文件当然就是cubemx文件(存放路径不要有中文名,要全英文)
core就是包含src和inc,即c文件和头文件的地方属于系统配置文件,包括着各种系统外设的初始化
Drivers芯片包文件
MDK-ARM工程文件以及各种Debug生成文件
User文件夹就是自己创建的文件

点进user文件夹中
RFID防伪设计(物联网工程课程设计)DAY4---LCD屏幕显示
oled就是所有有关oled的模块文件(OLCD)
bsp_system.h文件中包含着全部的自己创建的头文件
(未来大型工程,每次都要写一遍包含头文件太麻烦了)
codetable.h字模表,可以不用管,这是为了LCD中显示中文而用的,如果不用显示中文可以将这个头文件删除

买外设的时候必然会给你提供一定的例程文件
我这里就不再进行分析了,比较麻烦,有兴趣的自己看就行了
总之你需要在keil中有这几个文件
RFID防伪设计(物联网工程课程设计)DAY4---LCD屏幕显示
自己添加的就是这个OLED_I2C.c文件以及相关的头文件
在这个文件里面有所有有关OLED的函数
下面是OLED_I2C.c文件

#include "OLED_I2C.h"
#include "i2c.h"
#include "codetab.h"
#include "stm32f1xx_hal.h"
void WriteCmd(unsigned char I2C_OLED_COMmand)//写命令
{
	HAL_I2C_Mem_Write(&hi2c1,OLED_ADD,OLED_COM,I2C_MEMADD_SIZE_8BIT,&I2C_OLED_COMmand,1,100);
}
		
void WriteOLED_DAT(unsigned char I2C_OLED_DATa)//写数据
{
	HAL_I2C_Mem_Write(&hi2c1,OLED_ADD,OLED_DAT,I2C_MEMADD_SIZE_8BIT,&I2C_OLED_DATa,1,100);	
}
 
	void OLED_Init(void)
{
	HAL_Delay(100); //这里的延时很重要
	
	WriteCmd(0xAE); //display off
	WriteCmd(0x20);	//Set Memory Addressing Mode	
	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8);	//Set OLED_COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //亮度调节 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set OLED_COM pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vOLED_COMh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
}

void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{ 
	WriteCmd(0xb0+y);
	WriteCmd(((x&0xf0)>>4)|0x10);
	WriteCmd((x&0x0f)|0x01);
}

void OLED_Fill(unsigned char fill_OLED_DATa)//全屏填充
{
	unsigned char m,n;
	for(m=0;m<8;m++)
	{
		WriteCmd(0xb0+m);		//page0-page1
		WriteCmd(0x00);		//low column start address
		WriteCmd(0x10);		//high column start address
		for(n=0;n<128;n++)
			{
				WriteOLED_DAT(fill_OLED_DATa);
			}
	}
}


void OLED_CLS(void)//清屏
{
	OLED_Fill(0x00);
}

void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}

void OLED_OFF(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X10);  //关闭电荷泵
	WriteCmd(0XAE);  //OLED休眠
}


// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description    : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
	unsigned char c = 0,i = 0,j = 0;
	switch(TextSize)
	{
		case 1:
		{
			while(ch[j] != '\0')
			{
				c = ch[j] - 32;
				if(x > 126)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
					WriteOLED_DAT(F6x8[c][i]);
				x += 6;
				j++;
			}
		}break;
		case 2:
		{
			while(ch[j] != '\0')
			{
				c = ch[j] - 32;
				if(x > 120)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<8;i++)
					WriteOLED_DAT(F8X16[c*16+i]);
				OLED_SetPos(x,y+1);
				for(i=0;i<8;i++)
					WriteOLED_DAT(F8X16[c*16+i+8]);
				x += 8;
				j++;
			}
		}break;
	}
}


// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在.h中的索引
// Description    : 显示ASCII_8x16.h中的汉字,16*16点阵
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
	unsigned char wm=0;
	unsigned int  adder=32*N;
	OLED_SetPos(x , y);
	for(wm = 0;wm < 16;wm++)
	{
		WriteOLED_DAT(F16x16[adder]);
		adder += 1;
	}
	OLED_SetPos(x,y + 1);
	for(wm = 0;wm < 16;wm++)
	{
		WriteOLED_DAT(F16x16[adder]);
		adder += 1;
	}
}



// Parameters     : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description    : 显示BMP位图
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
	unsigned int j=0;
	unsigned char x,y;

  if(y1%8==0)
		y = y1/8;
  else
		y = y1/8 + 1;
	for(y=y0;y<y1;y++)
	{
		OLED_SetPos(x0,y);
    for(x=x0;x<x1;x++)
		{
			WriteOLED_DAT(BMP[j++]);
		}
	}
}






void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//得到偏移后的值			
		if(x>128-1){x=0;y=y+2;}
		if(Char_Size ==16)
			{
			OLED_SetPos(x,y);	
			for(i=0;i<8;i++)
			WriteOLED_DAT(F8X16[c*16+i]);
			OLED_SetPos(x,y+1);
			for(i=0;i<8;i++)
			WriteOLED_DAT(F8X16[c*16+i+8]);
			}
			else {	
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
				WriteOLED_DAT(F6x8[c][i]);
				
			}
}
u32 oled_pow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}	


//显示2个数字
//x,y :起点坐标	 
//len :数字的位数
//size:字体大小
//mode:模式	0,填充模式;1,叠加模式
//num:数值(0~4294967295);	 		  
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{         	
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
	}
} 

后面会有分析

OLED_I2C.h头文件

#ifndef __OLED_I2C_H
#define	__OLED_I2C_H

#include "i2c.h"
 
#define u8 uint8_t
#define u32 uint32_t

#define OLED_ADD	0x78  // OLED的I2C地址(禁止修改)
#define OLED_COM				0x00  // OLED 指令(禁止修改)
#define OLED_DAT 			0x40  // OLED 数据(禁止修改)

void WriteCmd(unsigned char I2C_Command);//写命令
void WriteDat(unsigned char I2C_Data);//写数据
void OLED_Init(void);//初始化
void OLED_SetPos(unsigned char x, unsigned char y);
void OLED_Fill(unsigned char fill_Data);//全屏填充
void OLED_CLS(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize);//显示字符串
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N);//显示汉字
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);//显示图片

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size);
u32 oled_pow(u8 m,u8 n);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2);//size2(16|12)

#endif

头文件主要是对c文件中定义的函数进行声明
同时定义一些变量
注意
#ifndef #define #endif
防止头文件被重复定义

bsp_system.h

#ifndef __BSP_SYSTEM_H
#define __BSP_SYSTEM_H

#include "stdio.h"
#include "OLED_I2C.h"

#endif /* __BSP_SYSTEM_H */

最后是main.c文件

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_system.h"


/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

uint8_t send_date[64] = {0};

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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_USART1_UART_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	
	OLED_Init();
  OLED_CLS();
	

  OLED_ShowStr(0,0,(unsigned char*)"Hello world",2);
 	OLED_ON(); 
	
	
  while (1)
  {
		
		//串口调试
		sprintf((char *)send_date,"test date\n");
		HAL_UART_Transmit(&huart1, send_date, sizeof(send_date), 100);
		//HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
		HAL_Delay(500);
		
		
    /* USER CODE END WHILE */

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

好,代码都上传完了,那么接下来就是代码解释
main文件和那些头文件没啥可说的
主要是程序移植过程中的OLED_I2C.c文件中的内容
看起来函数很多实际上就一两个函数需要修改
最底层的函数只有2个

void WriteCmd(unsigned char I2C_OLED_COMmand)//写命令
{
	HAL_I2C_Mem_Write(&hi2c1,OLED_ADD,OLED_COM,I2C_MEMADD_SIZE_8BIT,&I2C_OLED_COMmand,1,100);
}
		
void WriteOLED_DAT(unsigned char I2C_OLED_DATa)//写数据
{
	HAL_I2C_Mem_Write(&hi2c1,OLED_ADD,OLED_DAT,I2C_MEMADD_SIZE_8BIT,&I2C_OLED_DATa,1,100);	
}

一个函数是写指令一个是写数据
驱动OLCD实际上还是对寄存器进行读写,特定的寄存器有特定的地址
所以我们在头文件中定义的了相对应的地址

#define OLED_ADD	0x78  // OLED的I2C地址(禁止修改)
#define OLED_COM				0x00  // OLED 指令(禁止修改)
#define OLED_DAT 			0x40  // OLED 数据(禁止修改)

根据地址写入数据对屏幕进行驱动

HAL库相关函数为

HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);

阻塞模式直接对Mem进行读写

为什么野火例程会有那么长,你这里就这么点?
野火程序,是为了程序的健壮性,
例程例程,当然是给你最好的东西,防止出错,同时对时序进行一定判断
其实有部分程序已经在hal的函数中进行了集成,不需要自己考虑
很方便

效果

RFID防伪设计(物联网工程课程设计)DAY4---LCD屏幕显示

上一篇:这座城市多了十只伤心的鸽-Alpha冲刺Day4


下一篇:[QBXT游记]Day3 Test & Day4