0%

stm32_note_14day

=============================
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)
}