=============================
2022-9-1
=============================
一.温故知新
串行的接口
分类 : USART/UART、I²C、SPI、I²S、CAN、USB、SDI0、Eth
USART/UART驱动起来了->WiFi模块是USART的接口
I²C总线->使用GPIO模拟I²C的协议->E²PROM、OLD、SHT30
E²PROM
电可擦除可编程的只读存储器
通过官方手册了解E²PROM
1)E²PROM使用的是两线串行的接口(I²C的接口)
2)比较常见的是8管脚的封装形式
(VCC<接电源> GND<接地> A2 A1 A0<设备地址的输入>)
(SDA<数据线> SCL<时钟线> WP<写保护>)
3)规格
型号 规格 存储大小 页
AT24C01 1K 128byte 16page
AT24C02 2K 256byte 32page
AT24C04 4K 512byte 32page
AT24C08 8K 1024byte 64page
AT24C16 16K 2048byte 128page
4)设备地址
所有规格的E²PROM的设备地址高四位是固定的1010
后三位设备地址通过A2 A1 A0进行选择
最低位是读写标志位
5)读写操作
两种写操作
按字节写和按页写
三种读操作
当前地址读和随机地址读和顺序读
二.I²C设备之OLED
1.OLED的简介
OLED叫做有机发光二极管,在嵌入式领域中属于显示类型的设备
和LCD(液晶显示屏)显示的方法不同,
LCD在显示时需要背光灯,OLED在显示时不需要背光灯
显示屏是显示屏,触摸屏是触摸屏,是两个设备
如果将来操作的设备既有显示屏也有触摸屏,就要写两份驱动程序
一个是显示屏驱动,一个是触摸屏驱动
2.屏幕的显示
屏幕上是由N多个像素点组成的,所以大家平常所说的屏幕分辨率
指的是像素点的个数
1024×768
1920×1080
之所以屏幕可以显示出各种颜色是因为像素点会在显存中占用存储空间
像素点会在显存中占用多大的存储空间?
和像素点的位色有关(位色可以分为16位色和32位色 )
比如说显示设备是16位色,说明一个像素点在显存中占用16位(2byte)
比如说显示设备是32位色,说明一个像素点在显存中占用32位(4byte)
配色方案
常用的显示设备的配色方案(RGB YUV)
16位色RGB配色方案 XXXXX YYYYYY ZZZZZ
R G B
0xF800 全红
0x07E0 全绿
0x001F 全蓝
0xFFFF 全白
0x0000 全黑
32位色RGB配色方案 RRRRRRRR XXXXXXXX YYYYYYYY ZZZZZZZZ
保留的 R G B
0xFF0000 全红
0xFF00 全绿
0xFF 全蓝
3.开发板上的显示屏
STM32F103->OLED屏幕->0.96寸->128×64->I²C接口
STM32F407->LCD屏幕->2.8寸->320×240->FSMC(内存)接口
4.从官网找资料
OLED屏幕->亿阳电子
LCD(液晶显示器320 * 240)
接口
单片机:
uart(串口屏)
液晶显示器接的是串口,能通过串口把数据发送给LCD,就能够显示
例:通过串口把坐标发送过去,再把显示的字符(字的编码)发送过去
(一般来说这种屏都是自带字库的)
spi(SPI总线)
液晶显示器接的是SPI总线
sram(内存接口)
LCD连接到了主芯片的内存接口上的,访问LCD就和访问内存一样,
通过指针的方式去访问
SOC : RGB LVDS HDMI VGA MIPI
我们的stm32开发板的液晶显示用的是sram接口,相当于接到了内存接口上
所以我们先看一下内存的访问
内存的访问(通过三总线来访问)<01m4的总线和SRAM的总线>
m4的总线和SRAM的总线
addr 传输地址
--------------
--------------
--------------
--------------
data 传输数据
--------------
--------------
ctrl 控制读写时序/片选...
例:
假如执行*(int *)0x4 = 1;
三总线的电平信号应该是这样的
addr
第0位地址线--------------0
第1位地址线--------------0
第2位地址线--------------1
第3位地址线--------------0
data
第0位数据线--------------1
第1位数据线--------------0
假如执行*(int *)0xC = 3;
三总线的电平信号应该是这样的
addr
第0位地址线--------------0
第1位地址线--------------0
第2位地址线--------------1
第3位地址线--------------1
data
第0位数据线--------------1
第1位数据线--------------1
看<02LCD电路图>
02LCD电路图
简介
因为FSMC_A12这个管脚只区分地址线的第12位所以(1)和(2) | (3)和(4)执行效果一样
(1)(*(int *)0x1000) = 2;
(2)(*(int *)0x1001) = 2;
(3)(*(int *)0x0000) = 2;
(4)(*(int *)0x0001) = 2;
地址线只区分第12位是因为把这一位当作命令来使用,下面我们来区分
(1)和(3) | (2)和(4)的区别
LCD的背部是有一个控制芯片(LCD自带)的,
LCD模块上的引脚是连接控制芯片的,
控制芯片在控制LCD的显示,
我们开发板上使用的LCD控制芯片是9341
9341内部有很多寄存器、显存
当想要在LCD上画点首先需要指定坐标、横屏显示|竖屏显示...
-------------------------------
(100, 100) 竖 显示点(红色)
9341 寄存器0x1234 存储X轴坐标
*(int *)0x1234 = 100;
-------------------------------
这些信息都需要在9341中的寄存器里设置
我们之前操作GPIO知道,每一个寄存器都有一个地址,为了和我们的地址总线的地址区分,我们管寄存器的地址叫做[命令cmd]
当我们将来往寄存器里写数据,我们的LCD是这样规定的
1.先把命令写过来
2.再把数据写过来
---------------------------------------------------------------
例 : 假如寄存器的地址[命令]是0x10,想要给这个寄存器写数据2
0x10 <------- 2
不管是0x10还是数据2,都是要通过内存接口发送给LCD,走的都是数据线,对于STM32来说<0x10>和<2>都是数据,
所以发送时必须要区分开,发送的是命令还是数据(用的就是地址线的第12位来区分)
所以需要知道地址线的第12位是高电平表示命令,还是低电平表示命令
需要查看<LCD的手册>
地址线的第12位为1的时候表示数据
地址线的第12位为0的时候表示命令
1.先把命令写过来
(*(int *)0x0000) = 0x10;
2.再把数据写过来
(*(int *)0x1000) = 2
执行完这两步操作数据2就写到了<0x10>的那个寄存器中了
---------------------------------------------------------------
对于显存来说也是这样的方式,显存本身就是一段存储空间,内部就会有地址<命令>
如果在LCD上画一个点
1.首先把显存的<命令>发过去
2.再把数据发过去(点的坐标、颜色)
接下来我们来看看片选
片选
片选简介
内存控制器可以支持多个设备(SRAM LCD DM9000...)
我们查看<STM32手册>中的FSMC来看看我们的内存控制器,可以支持哪些设备
<P1191>-------PSRAM(4个存储区域)代表有4个片选
<P1192>-------NOR/PSRAM(我们的LCD接的是这块)
--->FSMC_NE[4:1]4个片选
--->FSMC_NBL[1:0]控制访问位宽的
(有可能接8位内存或者接16位内存)
<P1194>-------NOR/PSRAM
4 x 64MB可以接4片内存,每片64MB大小
内存的范围是0x6000 0000 - 0x6FFF FFFF
每一片内存的地址不会重复,所以
0x6000 0000 第一片内存起始地址
0x6400 0000 第二片内存起始地址
0x6800 0000 第三片内存起始地址
0x6C00 0000 第四片内存起始地址
-------------------------------
31 30 29 28 27 26 25 24
0 1 1 0 0 0 X X
0 1 1 0 0 1 X X
0 1 1 0 1 0 X X
0 1 1 0 1 1 X X
-------------------------------
其实当我们使用第一片内存的空间地址时,自动会让片选1有效(内部译码器已经规定好了)
我们通过LCD硬件原理图中得到 <FSMC_NE4>说明我们的LCD使用的是片选4
所以我们将来访问LCD就需要访问 0x6C00 0000 - 0x6FFF FFFF这个地址范围
通过上面一系列的信息(地址线 + 片选)我们知道了
往这个地址写的是 0x6C000000 命令
往这个地址写的是 0x6C001000 数据
<P1194>表185说的也是片选如果第26和27位
00 片选1
01 片选2
10 片选3
11 片选4
<P1195>表186说的是访问内存的位宽区别
当我们选择8位位宽来访问内存,会把地址线的0~25作为地址
如果选择16位的方式来访问,HADDR[25:1]>>1才是真实地址
---------------------------------------------------
原理:
我们在访问内存时,内存中有很多字节
如果按照8位(1字节)来访问内存
只看后四位
0x1000--------- 0000
0x1001--------- 0001
0x1002--------- 0010
0x1003--------- 0011
0x1004--------- 0100
0x1005--------- 0101
0x1006--------- 0110
0x1007--------- 0111
上面都是每一个字节的地址,如果按照16位(2字节)来访问就应该是
只看后四位
0x1000--------- 0000
0x1002--------- 0010
0x1004--------- 0100
0x1006--------- 0110
0x1008--------- 1000
0x100A--------- 1010
以16位方式来访问,最低位肯定是0,所以16位访问方式
地址线的[25:1]需要 右移 1位
---------------------------------------------------
所以经过上面位宽的选择我们之前分析的
往这个地址写的是 0x6C000000 命令
往这个地址写的是 0x6C001000 数据
有问题
写命令的没有问题,但是写数据的有问题
当我们选择16位的方式访问LCD时
把0x6C001000放到地址线的时候会变成0x6C000800
硬件会帮我们把[25:1]右移1位,所以就不符合我们的LCD了
所以我们在选择地址的时候应该选择0x6C002000
当我们写数据时这个地址右移1位就会变成0x6C001000
命令 0x6C000000
数据 0x6C002000
------------------------------------------
(100, 100) 0x1234
1.*(int *)0x6C000000 = 0x1234;
2.*(int *)0x6C002000 = 100;
把100写到0x1234
LCD_CMD= 0x1234;
LCD_DATA = 100;
------------------------------------------
LCD显示
LCD固件
在LCD上画的任何东西,都是一个点一个点画的(点阵)
颜色 RGB(通过三原色表示) YUV(通过分量来表示Y亮度 UV色度)
我们用的是RGB方式
RGB888
一个像素点占24位
11111111 11111111 11111111 白色
最红 最绿 最蓝
RGB565
一个像素点占16位
11111 111111 11111 白色
红 绿 蓝
我们的LCD(9341)用的是RGB565,分辨率是320 * 240
显存需要320 * 240 * 2(byte)字节
LCD调节亮度
lcd扫描
开发板俯视图
cfont文件中显示A
FSMC的配置
俯视图
取字模
固件分析 lcd.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const unsigned char shang[128] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x80,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0xFF,0xE0,0x00,0x00,0x80,0x00,
0x00,0xC0,0x80,0x00,0x00,0xE0,0x80,0x00,0x00,0x71,0x00,0x00,0x00,0x01,0x1C,0x00,
0x00,0x01,0x1F,0x80,0x10,0x01,0x11,0x00,0x1F,0xFF,0x11,0x00,0x1F,0xFF,0x11,0x00,
0x00,0x03,0x21,0x00,0x00,0x02,0x21,0x00,0x00,0x22,0x3F,0x00,0x00,0x42,0x3D,0x00,
0x00,0xC2,0x10,0x00,0x03,0x82,0x00,0x20,0x07,0x02,0x00,0x30,0x03,0x06,0x00,0x1C,
0x00,0x06,0x00,0x3C,0x00,0x07,0xFF,0xF8,0x00,0x07,0xFF,0xF0,0x00,0x03,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
const unsigned char guan[128] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,
0x00,0x44,0x04,0x00,0x00,0x42,0x18,0x00,0x00,0xC3,0x30,0x00,0x00,0x81,0xE0,0x00,
0x00,0x83,0xE0,0x00,0x01,0xFE,0x70,0x00,0x01,0xF0,0x3C,0x20,0x00,0xC0,0x00,0x40,
0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x03,0xFF,0xE1,0x80,0x03,0xFF,0x83,0x00,
0x02,0x00,0x0E,0x00,0x02,0x00,0x3C,0x00,0x02,0x7F,0xF0,0x00,0x04,0x7F,0x1F,0xE0,
0x04,0x00,0x1E,0x70,0x04,0x00,0x00,0x10,0x06,0x01,0xE0,0x18,0x07,0xFF,0xF0,0x18,
0x03,0x00,0x00,0x18,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x38,
0x00,0x00,0x00,0x70,0x00,0x00,0x03,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void move(参数)
{
圆心坐标 x ,y
dir = 3;
switch(dir)
{
case 1 :
case 2 :
case 3 :
if(y == 240 - r - 1)
dir = 2;
else if(x == 320 - r - 1)
dir = 4;
else
dir = 3;
break;
case 4 :
}
switch(dir)
{
case 1 : x--, y--; break;
case 2 : x++, y--; break;
case 3 : x++, y++; break;
case 4 : x--, y++; break;
}
lcd_draw_cir(x, y)
}