0%

=============================
2022-8-31
=============================

一.温故知新

    串口之I²C
    1.I²C总线中包含两条线(SDA数据线 和 SCL时钟线)
    2.I²C总线上可以连接很多的I²C接口的设备,设备与设备之间都串联起来的
    3.I²C总线规定了,凡是连接到I²C总线上的设备都需要有一个设备地址
        一般来说设备地址是7bit
    4.I²C总线上的设备需要区分主设备和从设备
    5.如果I²C总线上的两个设备都想要和另外的一个设备通信,会触发抢主机制
        抢主机制 : 谁先产生开始信号,谁就优先通信
    6.I²C总线规定了,在空闲状态下SDA和SCL要保持高电平的状态
        所以I²C总线需要外接上拉电阻
    7.开始信号和结束信号
        开始信号 : 在SCL为高电平时,SDA由高电平拉到低电平
        结束信号 : 在SCL为高电平时,SDA由低电平拉到高电平
    8.传输数据
        1)在SCL为低电平的时候,把数据放到SDA数据线上
        2)拉高SCL,延时一段时间,(当SCL为高电平时,SDA保持不变)
        3)拉低SCL,切换下一位数据放到SDA数据线上
    9.传输一个字节数据需要9个周期 = 8个数据周期 + 1个ACK周期
    A.I²C总线属于停等式的流控,传输完一个字节数据需要有应答
    B.产生ACK和读取ACK
        1)产生ACK : 在第9个周期,拉低SCL,把SDA也拉低,拉高SCL
        2)读取ACK : 在第9个周期,当SCL为高电平时,读取SDA的电平
    C.I²C总线是高位先出
    D.主设备给从设备发送数据
        1)主设备产生开始信号
        2)主设备发送从设备的设备地址 + 写的标志(0)
        3)主设备等待ACK
        4)主设备发送一个字节数据
        5)主设备等待ACK
        ...
        n)主设备发送最后一个字节数据
        n+1)主设备可以等待或者不等待ACK
        n+2)主设备产生结束信号
    E.主设备读取从设备的数据
        1)主设备产生开始信号
        2)主设备发送从设备的设备地址 + 读的标志(1)
        3)主设备等待ACK
        4)主设备等待读取一个字节数据
        5)主设备产生ACK
        ...
        n)主设备读取最后一个字节数据
        n+1)主设备不产生ACK
        n+2)主设备产生结束信号

二.I²C接口设备之E²PROM

    1.简单了解E²PROM
    E²PROM : 也叫做EEPROM,电可擦除可编程的只读存储器
    作用 : 是一种存储类的设备
    特点 : 存储到E²PROM中的数据,设备掉电之后,数据不会丢失
    注意 : E²PROM并不是硬盘
    应用 : 使用E²PROM存储特殊的数据
    (门禁打卡设备、测量设备、仪器仪表、用来存储BIOS或者BootLoader)
    
    2.ROM的发展历史
    ROM 只读存储器 特点是内部存储的数据只能读取,不能写入或者修改
    PROM 可编程的只读存储器 特点是内部存储的数据可以通过编程方法修改一次
    EPROM 可擦除可编程的只读存储器 特点是内部存储的数据可以修改多次
                    修改方法有限制
                    需要通过紫外线照射存储介质
    EEPROM 电可擦除可编程的只读存储器 特点是内部存储的数据可以修改多次
                    通过电信号即可清除数据
    3.通过手册了解E²PROM
    打开<STM32RBT6\DataSheet\其他>目录下
    <AT24C02_ATMEL Corporation.pdf>
    1)我们的开发板上使用的E²PROM是阿特梅尔公司的模块
    2)使用的是8管脚的封装形式
    (A0 A1 A2<设备地址> GND<接地> SDA<数据线> SCL<时钟线>)
    (WP(写保护) VCC<接电源>)
    3)使用的是I²C接口的E²PROM
    4)规格
    型号	规格	存储大小		Page
    AT24C01	1K	128byte		16个Page
    AT24C02	2K	256byte		32个Page
    AT24C04	4K	512byte		32个Page
    AT24C08	8K	1024byte		64个Page
    AT24C16	16K	2048byte		128个Page

eeprom

    5)任何规格的E²PROM的设备地址的高四位都是固定的了1010
        1K/2K规格可以通过A2 A1 A0管脚选择设备地址
        4K规格可以通过A2 A1管脚选择设备地址
        8K规格可以通过A2管脚选择设备地址
        16K规格没有管脚可以选择设备地址
    6)通过开发板原理图的解析可以了解
        我们开发板上的E²PROM的设备地址是
        10100000(写操作的设备地址)0xA0
        10100001(读操作的设备地址)0xA1
    7)写操作
        1>按字节来写
            a>产生开始信号
            b>发送设备地址 + 写的标志(10100000<0xA0>)
            c>等待ACK
            d>发送内部存储空间的地址(0x00 - 0xFF)
            e>等待ACK
            f>发送一个字节数据
            g>等待ACK
            h>产生结束信号
        2>按页来写(注意:有页内回滚)
            a>产生开始信号
            b>发送设备地址 + 写的标志(10100000<0xA0>)
            c>等待ACK
            d>发送内部存储空间的地址(0x00 - 0xFF)
            e>等待ACK
            f>发送一个字节数据
            g>等待ACK
            ...
            n>发送最后一个字节数据
            n+1>等待ACK
            n+2>产生结束信号

页内回滚

    8)读操作
        1>当前地址读
            a>产生开始信号
            b>发送设备地址 + 读的标志(10100001<0xA1>)
            c>等待ACK
            d>读取一个字节数据
            e>不产生ACK
            f>产生结束信号
        2>随机地址读
            a>产生开始信号
            b>发送设备地址 + 写的标志(10100000<0xA0>)
            c>等待ACK
            d>发送内部存储空间的地址
            e>等待ACK
            f>产生开始信号
            g>发送设备地址 + 读的标志(10100001<0xA1>)
            h>等待ACK
            i>读取一个字节数据
            j>不产生ACK
            k>产生结束信号
        3>顺序读(整个存储空间的回滚)
            可以直接拼接到当前地址读或者随机地址读的后面

三.使用GPIO模拟I²C的协议的程序->驱动E²PROM

=============================
2022-9-2
=============================

一.I²C接口设备之SHT30
    1.SHT30传感器的简介
    在我们使用的STM32F103开发板上有两个用来采集温湿度的传感器
    DHT11和SHT30
    DHT11传感器属于入门级别的传感器(做实验时会经常使用的传感器)
    SHT30传感器属于工业级传感器(做产品时会经常使用的传感器)
    2.从官网中查找资料
        1)测量范围 湿度:0%-100%	温度:-40℃ - 125℃
        2)误差范围 湿度:±2%		温度:±0.2℃
        3)通过温度的误差发现
        SHT30传感器,既可以采集到整数数据,也可以采集到小数数据
        4)SHT30传感器是I²C的接口
    3.通过手册了解SHT30
    打开<STM32RBT6\DataSheet\传感器>目录下<sht30_datasheet.pdf>
        1)8管脚的封装形式
        2)SHT30传感器本身是模拟型的传感器,
            内部自带AD转换,可以自行转换
        3)两种设备地址
        ---------------------------------------------
        手册中的描述
        如果ADDR管脚接VSS(接地)	使用的是设备地址A	0x44
        如果ADDR管脚接VDD(接电)	使用的是设备地址B	0x45
        ---------------------------------------------
        分析内容
        设备地址A	0x44	转换成二进制	01000100
        设备地址B	0x45	转换成二进制	01000101
        因为I²C总线规定设备地址是7bit的,最低位是读写标志位
        但是手册中说的设备地址A和设备地址B最低位被占用了
        所以0x44和0x45并不是最终的设备地址
        因为0x44和0x45最低位被占用了,
        用来区分设备地址A和设备地址B的,但是最高位没有被占用
        需要把设备地址A和设备地址B都左移一位
        设备地址A	01000100 << 1 10001000	0x88
        设备地址B	01000101 << 1 10001010	0x8A
        所以最终的设备地址是
        设备地址A	0x88
        设备地址B	0x8A
        ---------------------------------------------
        上述的内容将来在很多的嵌入式设备中都会碰到,要多留一个心眼!
        ---------------------------------------------
        4)经过开发板原理图的分析
        ADDR管脚连接了GND,所以我们开发板上的SHT30的设备地址是
        0x88
        5)设置采样频率
        需要发送采样的指令	0x2737
            1>产生开始信号
            2>发送设备地址 + 写的标志(0x88)
            3>等待ACK
            4>发送采样指令的高字节(0x27)
            5>等待ACK
            6>发送采样指令的低字节(0x37)
            7>等待ACK
            8>产生结束信号
        6)发送读取数据的指令
        读取数据的指令	0xE000
            1>产生开始信号
            2>发送设备地址 + 写的标志(0x88)
            3>等待ACK
            4>发送读取数据指令的高字节(0xE0)
            5>等待ACK
            6>发送读取数据指令的低字节(0x00)
            7>等待ACK
        7)读取数据
            1>产生开始信号
            2>发送设备地址 + 读的标志(0x89)
            3>等待ACK
            4>读取温度的高字节数据
            5>产生ACK
            6>读取温度的低字节数据
            7>产生ACK
            8>读取校验字节
            9>产生ACK
            A>读取湿度的高字节数据
            B>产生ACK
            C>读取湿度的低字节数据
            D>产生ACK
            E>读取检验字节
            F>不产生ACK
            G>产生结束信号
        8)读取出的数据不能直接使用,需要转换
        需要把温度的高字节和低字节的数据拼成一个2byte的数据
        需要把湿度的高字节和低字节的数据拼成一个2byte的数据
        代入公式转换
        湿度 = 100 * (double)(2byte湿度数据) / 65535;
        温度 = -45 + 175 * (double)(2byte温度数据) / 65535;
        9)最终结果的显示
        可以通过USART1或者OLED显示出来
==============================
任务
1.和同组的同学串一遍手册分析出的内容
2.搞一个新的项目工程,写SHT30的驱动程序
==============================

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

===========================
2022-8-26
===========================

一.温故知新

    74HC138->数码管的公共端
        是一个三八译码器,功能是一个选择器
        16管脚的封装形式
        VCC GND
        A0 A1 A2(三个输入)
        E1 E2 E3(控 模式)
        Y0 - Y7(8个输出)
    74HC595->数码管的数据段
        是一个可以把串行数据转换成并行数据的模块
        16管脚的封装形式
        VCC GND
        DS(串行数据的输入)
        Q0 - Q7(并行数据的输出)
        Q7’级联
        OE(输出使能)
        MR(主复位)
        SH_CP
        ST_CP
        由三个部分组成
        (<8bit移位寄存器><8bit存储寄存器><状态输出>)

选择器

    派发STM32F407ZGT6开发板

二.串行的接口

    1.串口的简介
    串口也叫做串行的接口,串行的通讯接口或者串行的通信接口
    (在Windows系统环境中串口表现为COM接口)
    (在Linux系统环境中串口表现为tty设备</dev/ttyxx>)
    串行的通信指的是数据一位一位的按顺序(高位先出 | 低位先出)进行传输
    串行的接口操作起来比较简单,只需要一条数据线就可以完成传输数据
    注意 : 并口也叫做并行的接口.指的是数据多位多位进行传输
    并不是说并口可以一次发送多位数据,就要比串口传输速度快,不一定
    2.串口的工作模式
        单工模式		A---->------B
        A设备只发送数据,B设备只接收数据,数据线上只有一种传输方向
        半双工模式	A-->-----<--B
        A设备可以收发数据,B设备也可以收发数据
        但是在同一时间,数据线上只能有一种传输方向
        发送的同时不能接收,接收的同时不能发送
        全双工模式	  ------>-----
                A	    B
                  ------<-----
        A设备可以收发数据,B设备也可以收发数据
        发送的同时可以接收,接收的同时可以发送
    3.不同电平标准的串口
    注意 : 如果两个设备需要通过串口进行通信,首先要确保电平标准一致
    如果电平标准不一致,需要使用电平转换模块
        TTL电平标准的串口
            0V代表数据0	3.3V/5V代表数据1
            传输距离差不多1米左右
        RS232电平标准的串口
            (3V - 12V)代表数据0	(-3V - -12V)代表数据1
            传输距离差不多15米左右
        RS485电平标准的串口(工业级的总线<抗干扰性极强>)
            在单工模式或者半双工模式下最少需要两条数据线
            在全双工模式下需要四条数据线
            因为传输的是两条电线的电压差
            传输距离差不多1000米左右
    4.我们开发板上串口的分类
    在STM32F103RBT6芯片中串口的分类
    SPI、I²C、I²S、USART/UART、CEC、USB、CAN、以太网接口、SDIO
    本次主要讲的是USART/UART
    5.USART/UART
    USART : 通用的同步异步收发器
        既支持同步模式也支持异步模式
    UART   : 通用的异步收发器
        只支持异步模式
    6.同步和异步
    同步 : 发送方发出数据之后,需要等待接收方回复应答,接收到应答之后再发送
        下一个数据包
        同步是阻塞模式
    比如说 : I²C、SPI属于同步的通信
    异步 : 发送方发出数据之后,不需要等待接收方回复应答,直接发送下一个数据包
        异步是非阻塞模式
    比如说 : UART属于异步的通信

    7.我们开发板上的USART/UART
    我们使用的STM32F103RBT6芯片中一共有3个USART/UART
    USART1	纽扣电池的上面
    USART2	无线模块的接口位置
    USART3	扩展IO
    目的 : 带着大家把USART1的实验完成,自己搞定USART2/USART3的驱动
    
    8.驱动USART1
    USART1_TX	PA9	推挽复用
    USART1_RX	PA10	浮空输入

    在通讯之前需要规定好双方的波特率
    115200 8N1
    115200	波特率
    8	8个数据位(1个字节数据)
    N	没有奇偶校验
    1	1个停止位
注意 : 上位机程序(串口调试助手)不要打开多个!!!

=================================
作业
1.把本周讲过的内容完整的复习一遍
2.举一反三(根据驱动USART1的程序把USART3搞定)
3.封装函数
    extern void usart_1_send_data(char *buf);//可以发送多个字节数据
4.通过USART1显示DHT11采集的数据
    "HUM:XX TEMP:XX"
5.通过USART1把结构体类型的数据发送出去
    struct data
    {
        float Hum;//湿度
        float Temp;//温度
        int pm_value;//PM2.5
        char buf[16];//存储额外的数据
    };
    struct data d = {23.45, 34.56, 9527, "Hello World"};
=================================

=============================
2022-8-29
=============================

一.温故知新

    串口
    1.以串行的方式(数据按照顺序<高位先出|低位先出>一位一位的进行传输)
    传输数据
    2.串口的工作模式(单工模式 半双工模式 全双工模式 )
    3.不同电平标准的串口(TTL、RS232、RS485)
    4.在STM32F103RBT6芯片中,串口外设
    (IIC、SPI、USART/UART、USB、CAN)
    5.研究USART/UART
    USART:通用的同步异步收发器
    UART:通用的异步收发器
    在STM32F103RBT6芯片中有3个USART/UART
    在开发板上的位置
        USART1	纽扣电池的上面
        USART2	无线模块的接口
        USART3	扩展IO上
    6.驱动USART1
    void usart_1_init(void)
    {
        1)通过APB2总线使能GPIOA组和USART1的时钟
        2)初始化GPIOA组的管脚
            1>把PA9管脚配置为推挽的复用模式
            2>把PA10管脚配置为浮空的输入模式
        3)初始化USART1
            1>选择115200的波特率
            2>关闭硬件流控
            3>选择发送和接收模式
            4>选择没有奇偶校验
            5>选择8个数据位
            6>选择1个停止位
        4)初始化NVIC
            1>配置组优先级和子优先级的所占比例
            2>初始化NVIC
                选择USART1的中断号
                使能该中断
                选择组优先级的级别
                选择子优先级的级别
        5)选择由USART1的接收触发中断
        6)使能USART1的功能
    }
    void usart_1_send_byte(u8 data)
    {
        1)把形参数据通过官方固件进行发送
        2)以死等的方式等待USART1的数据发送成功
        3)清除USART1等待发送成功的标志状态
    }
    u8 usart_1_recv_byte(void)
    {
        1)判断USART1是否接收到了数据
        2)接收数据
        3)清除USART1接收到数据的标志状态
        4)把接收到的数据返回
    }

3.封装函数
    extern void usart_1_send_data(char *buf);//可以发送多个字节数据
4.通过USART1显示DHT11采集的数据(自己找张纸写一下itoa函数的实现)
    "HUM:XX TEMP:XX"
5.通过USART1把结构体类型的数据发送出去
    struct data
    {
        float Hum;//湿度
        float Temp;//温度
        int pm_value;//PM2.5
        char buf[16];//存储额外的数据
    };
    struct data d = {23.45, 34.56, 9527, "Hello World"};

二.串口附加的内容

    int a = 100;
    printf("a = %d\n", a);
    printf("%d-%c-%s-%f\n", 9527, 'A', "Hello", 3.14);
    int printf(const void *format, ...)//不定参的函数
    {
        int i = 0;

        for(i = 0; format[i] != '\0'; i++)
        {
            if(format[i] == '%')
            {
                i++;
                switch(format[i])
                {
                    case 'd' : 
                    case 'c' :
                    case 's' :
                    case 'f' :
                }
            }
            else
            {
                fgetc();
            }
        }
    }


    1.标准库中没有实现itoa函数,但是有sprintf函数
    printf一族的函数
    printf->把双引号的内容打印输出到标准输出中
    sprintf->把双引号的内容打印输出到字符数组中
    fprintf->把双引号的内容打印输出到文件中
    如果在默认环境中直接使用printf或者scanf函数,单片机的程序会直接跑飞
    因为printf和scanf函数它的操作对象是标准输入和标准输出,
    是有操作系统的环境下,但是单片机是裸板,没有操作系统
    2.如果想要在STM32环境中使用printf和scanf函数,需要做一些特殊处理
    需要把printf函数和scanf函数重定向到串口的发送和串口的接收
    推荐两种方法
        1)使用微库(Micro Lib)
        可以使用微库来替代C语言的标准库,
        微库主要是给容量比较小的芯片进行使用的,
        但是如果芯片本身容量够用不建议使用微库,
        因为微库实现的函数相较于C语言标准库来说不完整,
        而且在使用一些大型功能的函数的时候,
        微库的效率要比C语言标准库低
            1>开启微库
            第三行菜单栏->Options for target->target菜单中->
            在右边栏中->选中USE Micro LIB
            2>把printf和scanf函数的实现重定向到
            USART1的发送和接收,
            把fgetc和fputc函数的功能重定向到
            USART1的发送和接收
            int fgetc(FILE *f);
            int fputc(int c, FILE *f);
        2)禁用半主机模式
        半主机模式是由ARM核心提供的一种机制
        主要是因为ARM核心在研发的时候,ARM公司并不确定将来会和
        哪家半导体厂商合作,也不确定将来半导体厂商会在芯片上连接
        什么样的输入设备和输出设备
        所以,ARM公司在研发核心的时候会启用一种半主机模式
        ARM平台会通过仿真器/调制器可以把ARM核心中的输入输出
        重定向到X86平台
        现在我们需要把输入输出重定向到USART1的发送和接收上
        所以需要禁用半主机模式
    

三.引出后续内容

    1.完成了发送字符和发送字符串的函数
        将来操作无线模块可以发送AT指令
        AT指令是由'A'和'T'组成的字符串
        SSID是路由器的名称,password是路由器的密码
        AT+CWJAP="SSID","password"
        "AT+CWJAP="Easthome","123456"\r\n"
    usart_2_send_data("AT+CWJAP="Easthome","123456"\r\n");

    usart_2_send_data("AT+CWJAP=");
    usart_2_send_byte('"');
    usart_2_send_data("Easthome");
    usart_2_send_byte('"');
    usart_2_send_byte(',');
    usart_2_send_byte('"');
    usart_2_send_data("123456");
    usart_2_send_byte('"');
    usart_2_send_data("\r\n");
===========================================
作业
    1.复习
    2.模拟工业物联网控制台指令收发器
    在串口调试助手中发送"L0_ON",开发板接收到之后点亮LED0
    在串口调试助手中发送"L1_ON",开发板接收到之后点亮LED1
    在串口调试助手中发送"L2_ON",开发板接收到之后点亮LED2
    在串口调试助手中发送"BE_ON",开发板接收到之后打开蜂鸣器
    在串口调试助手中发送"L0_OFF",开发板接收到之后关闭LED0
    在串口调试助手中发送"L1_OFF",开发板接收到之后关闭LED1
    在串口调试助手中发送"L2_OFF",开发板接收到之后关闭LED2
    在串口调试助手中发送"BE_OFF",开发板接收到之后关闭蜂鸣器
    3.模拟ESP8266内部的AT指令操作
    接收一个字符串->驱动WiFi(内部支持AT指令)
        在串口调试助手中发送"+IPD,10:0123456789"
    +IPD,XX:YYYYYYYYYY
    X部分是接收到的数据的字节数
    Y部分是真正的数据
    4.根据开发板原理图找到USART3的管脚,和组内成员测试
===========================================

===========================
2022-8-30
===========================

一.温故知新

    USART1的驱动
    初始化
    发送数据
        发送一个字节数据
        发送多个字节数据
            通过遍历有效数据
            把数据按照一个字节一个字节的方式进行发送
        printf进行重定向(重定向到USART1的发送)
            使用微库
            禁用半主机模式
            以上两种方法都需要重新实现fgetc以及fputc函数
    接收数据
        轮询接收数据
        中断接收数据
            使用有限状态机的方法解析字符串数据

    "+IPD,XX:YYYYYYY"
    XX指的是接收到数据的字节数
    YY指的是真实数据
    "+IPD,12:abcdefghijkl"

#define A_FLAG 0
#define I_FLAG 1
#define P_FLAG 2
#define D_FLAG 3
#define H_FLAG 4
#define M_FLAG 5
#define S_FLAG 6
int flag = A_FLAG;
char bytes[4] = {0};//存储数据的字节数(字符串版本)
int i = 0;//循环变量
int length = 0;//存储数据的字节数(整数版本)
char data[128] = {0};//存储数据

void recv_handler(u8 c)
{
    switch(flag)
    {
        case A_FLAG : 	if(c == '+')
                    flag = I_FLAG;
                break;
        case I_FLAG :	if(c == 'I')
                    flag = P_FLAG;
                else
                    flag = A_FLAG;
                break;
        case P_FLAG :	if(c == 'P')
                    flag = D_FLAG;
                else
                    flag = A_FLAG;
                break;
        case D_FLAG:	if(c == 'D')
                    flag = H_FLAG;
                else
                    flag = A_FLAG;
                break;
        case H_FLAG :	if(c == ',')
                    flag = M_FLAG;
                else
                    flag = A_FLAG;
                break;
        case M_FLAG :	if(c == ':')
                    flag = S_FLAG;
                else
                    bytes[i++] = c;
                break;
        case S_FLAG :	length = atoi(bytes);
                while(length--)
                    data[] = c;
    }
}

二.串口之I²C

    1.I²C的简介
    IIC也称为I2C或者I²C(专业的写法就是I²C),属于串行总线中的一种,
    也是数据按顺序一位一位进行发送
    I²C必须要有两条线(一条是数据线<SDA>、一条是时钟线<SCL>),是半双工的工作模式		
    2.I²C与USART的对比
    USART : 通用的同步异步收发器
    两条数据线(一条是发送、一条是接收)
    属于全双工的工作模式
    由于没有时钟线,所以设备通过USART通信之前需要规定好双方的波特率

    I²C : 集成电路总线
    两条线(一条是数据线<SDA>、一条是时钟线<SCL>)	
    属于半双工的工作模式
    由于有时钟线,可以自行调节读写速率,所以不需要规定双方的比特率

    3.I²C总线协议的目的
    需要驱动开发板上的OLED屏幕、EEPROM、SHT30
    1)需要带着大家把EEPROM的外设驱动起来
    2)需要带着大家把OLED的驱动移植成功
    3)自己想办法驱动SHT30
    
    4.通过官方手册了解I²C的协议
    打开<STM32RBT6\DataSheet\其他>目录下<IIC.pdf>P7 - P14中的图
    1)I²C总线需要两条线(SDA/SCL),在I²C总线上可以连接很多的设备,
        设备与设备之间是串联的关系
    2)凡是连接到I²C总线上的设备都需要有一个设备地址
        注意:一般来说设备地址是7bit的
    --------------------------------------------------------------------------
    面试题
    1.I²C总线理论上可以连接多少个设备?	理论上128个设备(设备地址)
    --------------------------------------------------------------------------
    3)I²C总线进行通信时会分为主设备和从设备
        主设备是主动的,从设备是被动的
        I²C总线本身非常公平,任何连接到I²C总线上的设备都既可以做主也可以做从
        (注意:有些设备由于本身的原因只能做从设备做不了主设备)
        如果两个设备同时都想要通信,会触发抢主的机制
        抢到主的设备优先通信,没抢到主的设备等待通信完成之后,再重新抢主
        (注意:抢主的机制,就是谁先产生开始信号,谁就优先通信)
    4)I²C总线在空闲状态时,SDA和SCL要呈现高电平的状态(需要外接上拉电阻)
    5)开始信号和结束信号
        开始信号 : 在SCL为高电平的时候,SDA由高电平拉到低电平
        结束信号 : 在SCL为高电平的时候,SDA由低电平拉到高电平
    6)传输数据
        1>在SCL为低电平的时候,把数据放到SDA数据线上
        2>拉高SCL,当SCL为高电平的时候,SDA的电平要保持不变
        3>拉低SCL,在SCL为低电平的时候,切换下一位数据放到SDA数据线上
    7)I²C总线是高位先出
    8)I²C总线在传输一个字节数据时需要9个周期
        9个周期 = 8个数据周期 + 1个ACK(应答)周期
    9)I²C总线属于停等式的流控,每发送完一个字节数据都需要有一个ACK应答
    A)产生应答 : 在SCL的第9个周期,SCL拉低,从设备拉低SDA,拉高SCL.延时一段时间
        把SCL拉低,从设备拉高SDA
    B)读取应答 : 在SCL的第9个周期,在SCL为高电平时读取SDA的电平
        如果读取到了高电平代表没有ACK,如果读取到了低电平代表有ACK
    C)I²C总线产生开始信号之后,
        发送的第一个字节内容是 = 7bit设备地址 + 1bit读写标志位
    D)主设备发送数据给从设备
        1>主设备产生开始信号
        2>主设备发送从设备的设备地址 + 写的标志(0)
        3>主设备等待ACK
        4>主设备发送一个字节数据
        5>主设备等待ACK
        ...
        n>主设备发送最后一个字节数据
        n+1>主设备可以等待ACK或者不等待ACK
        n+2>主设备产生结束信号
    E)主设备读取从设备发送的数据
        1>主设备产生开始信号
        2>主设备发送从设备的设备地址 + 读的标志(1)
        3>主设备等待ACK
        4>主设备等待接收一个字节数据
        5>主设备产生ACK
        ...
        n>主设备等待接收最后一个字节数据
        n+1>主设备不需要产生ACK
        n+2>主设备产生结束信号

IIC

02.IIC硬件连接

03.IIC协议要求

04.IIC协议(开始信号结束信号)

05.IIC协议_详细

06.IIC协议_ACK产生

07.IIC_主的编程

开发板上的IIC设备

学生发的笔试题

===========================
2022-8-24
===========================

一.温故知新

    其他的定时器主要讲的是看门狗
    1.通过最新选型手册了解->有两个看门狗外设资源->属于定时器外设
    2.看门狗使用的环境
    3.喂狗的操作
    4.独立看门狗和窗口看门狗的区别
    5.独立看门狗
    IWDG_KR(键寄存器)	<接收特定的值,执行特定的操作>
    0x5555	开启(IWDG_PR和IWDG_RLR寄存器)写访问(关闭写保护)
    0xCCCC	开启看门狗
    0xAAAA	喂狗的操作
    IWDG_PR(预分频值寄存器) <用来给时钟频率分频的>
    000 4分频		100 64分频
    001 8分频		101 128分频
    010 16分频	110 256分频
    011 32分频	111 245分频
    IWDG_RLR(重装载值寄存器) <用来保存计数值的>
    计数值范围[12个0 - 12个1]<0 - 4095>
    
    传感器
    市面上比较常见的传感器
    传感器的接口
    通过DHT11的手册分析DHT11传感器
    1.温湿度的传感器
    2.4管脚的封装形式
        1PIN	VCC
        2PIN	DATA
        3PIN	NC
        4PIN	GND
    3.测量范围 : 湿度20 - 90%  温度0 - 50℃
    4.误差范围 : 湿度±5%         温度±2℃
    5.分辨力1 : 只能采集到整数的数据,采集不了小数数据
    6.串行接口(单线双向)
        串行接口 : 以串行的方式传输数据,数据按照顺序一位一位发送
        单线双向 : 半双工的工作模式
    7.高位先出
    8.一次完整的数据需要40bit = 8bit湿度的整数数据 + 8bit湿度的小数数据
            + 8bit温度的整数数据 + 8bit温度的小数数据
            + 8bit校验和
    9.分析读写时序
    10.串行接口发送数据接收数据(高位先出、低位先出)

二.驱动DHT11

三.DS18B20

    专门用来检测温度的传感器
    打开<STM32RBT6\DataSheet\传感器>目录下<DS18B20.pdf>
    1.DS18B20是3管脚的封装形式(VCC DQ GND)
    2.DS18B20是一线传输的传感器(只有一条数据线<既需要输入也需要输出>)
    3.电压范围		3.0V - 5.5V
    4.测量范围		-55℃ - +125℃
    5.误差范围(在-10℃ - +85℃)会有±0.5℃的误差
    6.DS18B20既可以采集到整数数据也可以采集到小数数据
    7.DS18B20采集到的数据会存放在9bit - 12bit的存储空间中
    8.转换12bit的温度数据最多需要消耗750ms
    9.在DS18B20传感器内部有9个字节的暂存器
        第0个字节		温度的低字节数据
        第1个字节		温度的高字节数据
        第2个字节		温度的上限
        第3个字节		温度的下限
        第4个字节		配置
        第5 6 7字节	保留
        第8个字节		校验
    A.通过配置暂存器的第4个字节(配置)来选择采集温度数据所占位数
        0 R1 R0 1 1 1 1 1
        第0-4位		都是1
        通过R1 R0选择温度所占位数
        0 0		9bit
        0 1		10bit
        1 0		11bit
        1 1		12bit
        第7位		是0
    B.用来存储温度的暂存器
        温度的高字节的高5位是符号位(0代表正	1代表负)
        DS18B20的分辨力是0.0625℃
        DS18B20的暂存器存储的也是补码数据
        所以将来从DS18B20的暂存器中读出的数据是补码
    -0.5°C
    原码	11111000 00001000
    补码	11111111 11111000
    注意 : 读取数据有一个特殊值
    假如将来读到的数据是
        11111111 00000000
    补码	11111111 00000000
    原码	11111001 00000000
    因为高字节和低字节的数据都要+1
        低字节的+1 : 补码转换成原码时候的+1
        高字节的+1 : 是因为产生了进位所以要+1
    C.DS18B20内部支持很多的指令
    D.手册中给驱动工程师写了案例
    E.分析DS18B20的时序图

四.数码管

    1.显示类的设备
    LCD显示屏、OLED屏幕、段码屏(需要厂家定制)、点阵屏、光立方、数码管
    作用就是用来显示数据的
    其中数码管可以显示数字0 - 9包括一些简单的英文字母
    2.数码管
    常见的数码管有七段数码管或者八段数码管
    其中每一个段就相当于是一个LED灯
    八段数码管要比七段数码管多一个用于显示小数的小数点
    3.共阴极数码管和共阳极数码管
    共阴极数码管 : 数码管中所有数据段的阴极都连接到了一起,接地
            给数据段的阳极输出高电平,可以点亮数码管
    共阳极数码管 : 数码管中所有数据段的阳极都连接到了一起,接电源
            给数据段的阴极输出低电平,可以点亮数码管
    各自的编码 : 
    共阳极数码管的编码
    uchar code table[]={0xc0,0xf9,0xa4,0xb0,//0 1 2 3
            0x99,0x92,0x82,0xf8,//4 5 6 7
            0x80,0x90,0x88,0x83,//8 9 A B
            0xc6,0xa1,0x86,0x8e};//C D E F
    共阴极数码管的编码:
    uchar code table[]={0x3f,0x06,0x5b,0x4f,//0 1 2 3
            0x66,0x6d,0x7d,0x07,//4 5 6 7
            0x7f,0x6f,0x77,0x7c,//8 9 A B
            0x39,0x5e,0x79,0x71};//C D E F
    4.四位共阴极数码管
    我们的开发板上连接的是四位共阴极的数码管
    产生的问题
    如果四位数码管的所有数据段都连接到芯片的管脚上就会占用32个管脚
    这样对于芯片的管脚太浪费了
    ------------------------------------------------------------------------
    为了解决芯片管脚浪费的问题
    把四位数码管的所有的A段都连接到了一起
    把四位数码管的所有的B段都连接到了一起
    ...
    把四位数码管的所有的DP段都连接到了一起
    这样处理只会消耗芯片的8个管脚
    产生的问题
    给8个数据段写入编码,四个数码管会显示相同的数据
    ------------------------------------------------------------------------
    四位数码管的公共端接到芯片的管脚上
    将来想要让某个数码管显示数据,就让该数码管的公共端有效,
    其他数码管的公共端无效即可
    产生的问题
    四位数码管不能同时显示数据(只能一个一个显示)
    ------------------------------------------------------------------------
    可以利用人类眼睛的视觉暂留处理

=============================
作业
1.把今天讲过的内容完完整整的复习一遍
=============================

===========================
2022-8-25
===========================

一.温故知新

    1.根据分析的DHT11的读写时序来写DHT11传感器的驱动
    2.分析DS18B20传感器的内容
        1)三管脚的封装形式(VCC DQ GND)
        2)一线传输(以串行的方式传输数据)<半双工的工作模式>
        3)工作电压范围	3.0V - 5.5V
        4)测量范围	-55℃ - +125℃
        5)误差范围(在-10℃ - 85℃)会有±0.5℃的误差
        6)可以采集到整数的数据和小数的数据
        7)在DS18B20的内部会有9个字节的暂存器
        第0个字节		温度的低字节数据
        第1个字节		温度的高字节数据
            高字节的高5位是符号位
            存储的是温度的补码数据
        第2个字节		温度的上限
        第3个字节		温度的下限
        第4个字节		配置
            是用来配置DS18B20采集温度所占位数
            0 R1 R0 1 1 1 1 1
            00	9bit的温度数据
            01	10bit的温度数据
            10	11bit的温度数据
            11	12bit的温度数据
        第5 6 7字节	保留
        第8个字节		检验
        8)在DS18B20传感器中支持很多的指令
        9)DS18B20官方手册给了发送指令的案例
        A)分析DS18B20的读写时序

二.数码管

    我们的开发板上使用的四位共阴极的数码管
    1.显示类的设备
    市面上的显示类型的设备有很多种
    LCD、OLED、数码管、段码屏、点阵屏、光立方...
    2.数码管
    数码管可以分为七段数码管和八段数码管
    八段数码管要比七段数码管多一个用于显示小数的小数点
    数码管中的每一个段就相当于是一个LED灯
    3.共阴极数码管和共阳极数码管
    共阴极数码管 : 数码管上所有的LED灯的阴极都连接到了一起,接地
    共阳极数码管 : 数码管上所有的LED灯的阳极都连接到了一起,接电源
    共阴极数码管的编码和共阳极数码管的编码正好相反
    共阳极数码管的编码 :
    uchar code table[]={0xc0,0xf9,0xa4,0xb0,//0 1 2 3
            0x99,0x92,0x82,0xf8,//4 5 6 7
            0x80,0x90,0x88,0x83,//8 9 A B
            0xc6,0xa1,0x86,0x8e};//C D E F
    共阴极数码管的编码 :
    uchar code table[]={0x3f,0x06,0x5b,0x4f,//0 1 2 3
            0x66,0x6d,0x7d,0x07,//4 5 6 7
            0x7f,0x6f,0x77,0x7c,//8 9 A B
            0x39,0x5e,0x79,0x71};//C D E F
    如何获取数码管的编码
    1)可以百度查找
    2)可以通过转换软件直接转换
    4.四位数码管
    四位数码管可以分别显示千位 百位 十位 个位
    已知一个数码管有8个LED灯需要8个数据段
    四位数码管有32个LED灯需要32个数据段
    如果直接连接到芯片的管脚上需要消耗芯片的32个管脚
    这样的方式对于芯片的管脚来说太浪费了
    5.四位数码管的问题

原理图

        1)芯片管脚的浪费
        把所有数码管的A段都连接到一起
        把所有数码管的B段都连接到一起
        ...
        把所有数码管的DP段都连接到一起
        2)相同数据段都连接到了一起
        给四位数码管一个数据编码,这4个数码管会显示相同的数据
        3)把四位数码管的4个公共端也都连接到GPIO的管脚上
        将来想要让某个数码管显示数据,就让给数码管的公共端输出低电平
        其他数码管输出高电平
        4)经过2) 3)的处理产生了一个问题,
            四位数码管不能同时显示不同的数据
        利用人眼睛的视觉暂留
    6.分析开发板原理图
        开发板上的四位共阴极的数码管
        公共端连接到了74HC138模块上
        数据段连接到了74HC595模块上
        如果想要驱动数码管,首先需要驱动74HC138和74HC595
    7.分析74HC138

74HC138

        74HC138是一个典型的三八译码器(是作为一个选择器来使用的)
        三个输入,八个输出(给一定的输入就会用相应的输出)
    打开<STM32RBT6\DataSheet\其他>目录下<74HC138.pdf>
        P3	封装形式(了解管脚功能)
        P4	真值表
        查看开发板原理图
        
    8.

74HC595

        74HC595是一个可以把串行数据转换成并行数据的模块
    打开<STM32RBT6\DataSheet\其他>目录下<74HC595.pdf>
        P4	封装形式(了解管脚功能)
        P5	工作原理分析
        
    9.根据开发板原理图把需要操作的管脚找到
    HC138_A0	PC11	74HC138中的控制	推挽输出
    HC138_A1	PC12	74HC138中的控制	推挽输出
    HC138_A2	PD2	74HC138中的控制	推挽输出
    HC595_SCLK	PB5	移位寄存器的时钟	推挽输出
    HC595_LCLK	PB4	存储寄存器的时钟	推挽输出
    HC595_DATA	PB3	把串行数据存储到	推挽输出
                移位寄存器中

==============================
任务
1.把9527显示到数码管上
2.把DHT11采集的温湿度的数据显示到数码管上
==============================
作业
1.把今天讲过的内容完整的复习一遍
2.把开发板作为记秒表的设备
    KEY0	开始记秒表
    KEY1	暂停
    KEY2	清零
    00.00
    秒数.
==============================

===========================
2022-8-17
===========================

一.温故知新

    从表象到里象的方式去了解设备
    1.从外观入手
        开发板上外接了哪些模块、使用芯片的型号
    2.画图了解芯片内部结构
        CPU + 内存 + 硬盘 + 外设
    3.介绍了ARM公司和ST公司
        ARM-CORTEX-A	针对高端应用(手机 平板电脑 安卓电视)
        ARM-CORTEX-R	针对实时操作系统(军工、航天、医疗)
        ARM-CORTEX-M	针对工控领域(智能终端设备)	
    4.通过手册了解芯片
        最新选型手册 : 外设数量、存储大小、封装形式
    5.开发方式
    6.搭建模板项目工程

二.通过硬件的手册了解芯片内部的结构

    打开<STM32RBT6\DataSheet\STM32>目录下<STM32F103RB.pdf>
    P19	芯片内部的结构
    
    1.STGM32F103RBT6芯片使用的CPU是ARM-CORTEX-M3主频是72MHz
    2.CPU是通过三总线来访问外设的
    3.三总线包括 : 数据总线(传输数据) 地址总线(传输地址) 控制总线(控制读写)
    --------------------------------------------------------
    例子 :
    在某工程中,要求给地址为0xaa55的空间复制一个整型的数据,
    数据为0x55aa
    0xaa55 = 0x55aa;.//不可以,因为左值是一个常量
    *(volatile unsigned int *)(0xaa55) = 0x55aa;
    --------------------------------------------------------
    4.在STM32芯片中AHB AHB1 AHB2都是三总线,叫做高速总线
        APB1 APB2也是三总线,叫做外设总线
    5.GPIO的管脚是从0开始计数的
        GPIOxy
        [x = A B C D]
        [y = 0 1 2 ... 15]
    6.大部分的外设都是连接到了外设总线上,所以当某个外设工作时
    需要先通过相应的外设总线打开外设的时钟,该外设才能工作
    
    P23	芯片的管脚
    GPIOA	[0 - 15]
    GPIOB	[0 - 15]
    GPIOC	[0 - 15]
    GPIOD	[0 1 2]

    P26	芯片管脚的复用
    芯片的管脚是多功能复用型的管脚
        输入功能
            上拉输入
            下拉输入
            浮空输入
        输出功能
            推挽输出
            开漏输出
        复用功能
            推挽复用
            开漏复用
        模拟功能
            模拟输入

三.GPIO外设之输出功能

    输入功能相当于是检测,输出功能相当于是控制
    输出功能分为推挽输出和开漏(漏级)输出
    推挽输出 : 管脚既可以输出高电平,也可以输出低电平
    开漏输出 : 管脚只能输出低电平,输出不了高电平
    LED->PC9管脚
    如果PC9管脚输出高电平,LED灯灭
    如果PC9管脚输出低电平,LED灯亮

01LED灯

    问题 : 芯片的管脚是多功能复用型的管脚,如何给管脚选择输出功能?
    答 : 可以通过操作GPIO外设的寄存器来选择输出的功能
    问题 : GPIO都有哪些寄存器?
    答 : 可以通过查询芯片的手册了解GPIO的寄存器以及操作方式
打开<STM32RBT6\DataSheet\STM32>目录下<STM32F10x中文参考手册.pdf>
寄存器的真实地址 = 基地址 + 偏移地址
例如 : 
GPIOC_CRL的真实地址 = GPIOC组的基地址 + CRL寄存器的偏移地址
        = 0x40011000 + 0x00
因为需要操作PC9管脚,所以需要使用端口配置高寄存器
GPIOC_CRH的真实地址 = GPIOC组的基地址 + CRH寄存器的偏移地址
        = 0x40011000 + 0x04
#define GPIOC_CRH *(volatile unsigned int *)(0x40011000 + 0x04)
GPIOC_CRH |= (3 << 4);//把PC9管脚配置为输出功能并且为50MHz的输出速率
-----------------------------------------------------------------------------
3		00000000 00000000 00000000 00000011
3<<4		00000000 00000000 00000000 00110000
    |
GPIOC_CRH	xxxxxxxx xxxxxxxx xxxxxxxx xx##xxxx
    =
GPIOC_CRH	xxxxxxxx xxxxxxxx xxxxxxxx xx11xxxx
-----------------------------------------------------------------------------
GPIOC_CRH &= ~(3 << 6);.//把PC9管脚配置为推挽输出
-----------------------------------------------------------------------------
3		00000000 00000000 00000000 00000011
3<<6		00000000 00000000 00000000 11000000
~(3<<6)		11111111 11111111 11111111 00111111
    &
GPIOC_CRH	xxxxxxxx xxxxxxxx xxxxxxxx ##11xxxx
    =
GPIOC_CRH	xxxxxxxx xxxxxxxx xxxxxxxx 0011xxxx
-----------------------------------------------------------------------------
#define GPIOC_ODR *(volatile unsigned int *)(0x40011000 + 0x0C)
GPIOC_ODR &= ~(1 << 9);//把PC9管脚输出低电平(亮灯)

GPIOC_ODR |= (1 << 9);//把PC9管脚输出高电平(灭灯)

四.通过开发板原理图查看芯片的管脚

    打开<STM32RBT6\开发板原理图>目录下<MCU.pdf>和<Periph.pdf>
    LED0	PC1
    LED1	PC2
    LED2	PC3
    输出高电平,灯亮
    输出低电平,灯灭
    需要操作GPIOC_CRL(端口配置低寄存器)以及
        GPIOC_ODR(端口输出数据寄存器)

五.写程序驱动LED灯

    1.通过开发板原理图找到LED灯连接的管脚
    2.分析模块的工作方式
    3.把最近的一个项目工程拷贝一份重命名一下
    4.在<02led/mylib>目录中新建<led.c>和<led.h>
    5.在<02led/project>目录中找到项目工程文件,并且打开
    6.在Keil的左边侧边栏中的mylib组内添加led.c文件

六.举一反三

    大家通过<02led>的项目工程,尝试着搭建<03beep>的项目工程
    效果
    在流水灯的基础上加入蜂鸣器
    0 1 2
    0->1->2->0->1->2
    灯亮的同时蜂鸣器响,灯灭的同时蜂鸣器关闭

=================================
作业
1.把今天讲过的内容完完整整的复习一遍
2.把举一反三的内容做出来
3.刷题
课外
自己找音乐的编码把音乐通过蜂鸣器播放出来
=================================