当前位置:   article > 正文

STM32——智能小车_stm32智能小车代码

stm32智能小车代码

STM32——智能小车

硬件接线

B-1A – PB0

B-1B – PB1

A-1A – PB2

A-1B – PB10

其余接线参考51单片机小车项目。

1.让小车动起来

motor.c

#include "motor.h"
void goForward(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goBack(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
}
void goLeft(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goRight(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void stop(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
  • 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

motor.h

#ifndef __MOTOR_H__
#define __MOTOR_H__
#include "main.h"
void goForward(void);
void goBack(void);
void goLeft(void);
void goRight(void);
void stop(void);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

main.c

#include "motor.h"
//main函数的while循环部分:
while (1)
{
    /* USER CODE END WHILE */
    goForward();
    HAL_Delay(1000);
    goBack();
    HAL_Delay(1000);
    goLeft();
    HAL_Delay(1000);
    goRight();
    HAL_Delay(1000);
    stop();
    HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.串口控制小车

uart.c

#include "string.h"
#include "stdio.h"
#include "motor.h"
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
#define SIZE 12
char buffer[SIZE];
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    // 判断中断是由哪个串口触发的
    if(huart->Instance == USART1)
    {
        // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
        if((UART1_RX_STA & 0x8000) == 0)
        {
            // 如果已经收到了 0x0d (回车),
            if(UART1_RX_STA & 0x4000)
            {
                // 则接着判断是否收到 0x0a (换行)
                if(buf == 0x0a)
                {
                    // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
                    UART1_RX_STA |= 0x8000;
                    // 灯控指令
                    if(!strcmp(UART1_RX_Buffer, "M1"))
                        goForward();
                    else if(!strcmp(UART1_RX_Buffer, "M2"))
                        goBack();
                    else if(!strcmp(UART1_RX_Buffer, "M3"))
                        goLeft();
                    else if(!strcmp(UART1_RX_Buffer, "M4"))
                        goRight();
                    else
                        stop();
                    memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
                    UART1_RX_STA = 0;
                }
                else
                    // 否则认为接收错误,重新开始
                    UART1_RX_STA = 0;
            }
            else // 如果没有收到了 0x0d (回车)
            {
                //则先判断收到的这个字符是否是 0x0d (回车)
                if(buf == 0x0d)
                {
                    // 是的话则将 bit14 位置为1
                    UART1_RX_STA |= 0x4000;
                }
                else
                {
                    // 否则将接收到的数据保存在缓存数组里
                    UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
                    UART1_RX_STA++;
                    // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
                    if(UART1_RX_STA > UART1_REC_LEN - 1)
                        UART1_RX_STA = 0;
                }
            }
        }
        // 重新开启中断
        HAL_UART_Receive_IT(&huart1, &buf, 1);
    }
}
int fputc(int ch, FILE *f)
{
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);
    return ch;
}
  • 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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

main.c

#include "motor.h"
extern uint8_t buf;
//main函数
HAL_UART_Receive_IT(&huart1, &buf, 1);
  • 1
  • 2
  • 3
  • 4

3.点动控制小车

uart.c

if (!strcmp(UART1_RX_Buffer, "M1"))
{
    goForward();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M2"))
{
    goBack();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M3"))
{
    goLeft();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M4"))
{
    goRight();
    HAL_Delay(10);
}
else
    stop();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

mian.c

// main函数里
HAL_NVIC_SetPriority(SysTick_IRQn,0,0); //或者通过cubeMX配置
while(1)
{
    stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.硬件PWM调速

硬件接线

B-1A – PA0

B-1B – PB1

A-1A – PA1

A-1B – PB10

其余接线参考上官一号小车项目。

main.c

// main函数里
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
while (1)
{
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 8);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 8);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 10);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 10);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 15);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 15);
    HAL_Delay(1000);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5.左右轮各自调速

main.c

// main函数里
while (1)
{
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
    HAL_Delay(1000);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

6.循迹小车

硬件接线

B-1A – PB0

B-1B – PB1

A-1A – PB2

A-1B – PB10

循迹模块(左) – PB3

循迹模块(右) – PB4

#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
// main函数里
while (1)
{
    if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
        goForward();
    if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
        goLeft();
    if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
        goRight();
    if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
        stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

7.循迹小车解决转弯平滑问题

硬件接线

B-1A – PA0

B-1B – PB1

A-1A – PA1

A-1B – PB10

循迹模块(左) – PB3

循迹模块(右) – PB4

#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
// main函数里
while (1)
{
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,19);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,19);
    }
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
    }
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
    }
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,0);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,0);
    }
}
  • 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

8.跟随小车

硬件接线

B-1A – PB0

B-1B – PB1

A-1A – PB2

A-1B – PB10

跟随模块(左) – PB5

跟随模块(右) – PB6

#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6)
// main函数里
while (1)
{
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
        goForward();
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
        goRight();
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
        goLeft();
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
        stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

9.摇头避障小车

硬件接线

sg90 – PB9

sg90.c

#include "sg90.h"
#include "gpio.h"
#include "tim.h"
void initSG90(void)
{
    HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}
void sgMiddle(void)
{
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}
void sgRight(void)
{
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
}
void sgLeft(void)
{
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); //将舵机置为180度
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

SG90.h

#ifndef __SG90_H__
#define __SG90_H__
void initSG90(void);
void sgMiddle(void);
void sgRight(void);
void sgLeft(void);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

main.c

initSG90();
HAL_Delay(1000);
while (1)
{
    sgLeft();
    HAL_Delay(1000);
    sgMiddle();
    HAL_Delay(1000);
    sgRight();
    HAL_Delay(1000);
    sgMiddle();
    HAL_Delay(1000);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

封装超声波传感器

超声波模块:

Trig – PB7

Echo – PB8

#include "sr04.h"
#include "gpio.h"
#include "tim.h"
//使用TIM2来做us级延时函数
void TIM2_Delay_us(uint16_t n_us)
{
    /* 使能定时器2计数 */
    __HAL_TIM_ENABLE(&htim2);
    __HAL_TIM_SetCounter(&htim2, 0);
    while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
    /* 关闭定时器2计数 */
    __HAL_TIM_DISABLE(&htim2);
}
double get_distance(void)
{
    int cnt=0;
    //1. Trig ,给Trig端口至少10us的高电平
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);//拉高
    TIM2_Delay_us(20);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);//拉低
    //2. echo由低电平跳转到高电平,表示开始发送波
    //波发出去的那一下,开始启动定时器
    while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET);//等待输入电平拉高
    HAL_TIM_Base_Start(&htim2);
    __HAL_TIM_SetCounter(&htim2,0);
    //3. 由高电平跳转回低电平,表示波回来了
    while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET);//等待输入电平变低
    //波回来的那一下,我们开始停止定时器
    HAL_TIM_Base_Stop(&htim2);
    //4. 计算出中间经过多少时间
    cnt = __HAL_TIM_GetCounter(&htim2);
    //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
    return (cnt*340/2*0.000001*100); //单位:cm
}
  • 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

sr04.h

#ifndef __SR04_H__
#define __SR04_H__
double get_distance(void);
#endif
  • 1
  • 2
  • 3
  • 4

main.c

while (1)
{
    if(dir != MIDDLE){
        sgMiddle();
        dir = MIDDLE;
        HAL_Delay(300);
    }
    disMiddle = get_distance();
    if(disMiddle > 35){
        //前进
    }
    else
    {
        //停止
        //测左边距离
        sgLeft();
        HAL_Delay(300);
        disLeft = get_distance();
        sgMiddle();
        HAL_Delay(300);
        sgRight();
        dir = RIGHT;
        HAL_Delay(300);
        disRight = get_distance();
    }
}
  • 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

封装电机驱动

硬件接线

与 “让小车动起来” 完全一样

B-1A – PB0

B-1B – PB1

A-1A – PB2

A-1B – PB10

while (1)
{
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    if(dir != MIDDLE){
        sgMiddle();
        dir = MIDDLE;
        HAL_Delay(300);
    }
    disMiddle = get_distance();
    if(disMiddle > 35){
        //前进
        goForward();
    }else if(disMiddle < 10){
        goBack();
    }else
    {
        //停止
        stop();
        //测左边距离
        sgLeft();
        HAL_Delay(300);
        disLeft = get_distance();
        sgMiddle();
        HAL_Delay(300);
        sgRight();
        dir = RIGHT;
        HAL_Delay(300);
        disRight = get_distance();
        if(disLeft < disRight){
            goRight();
            HAL_Delay(150);
            stop();
        }
        if(disRight < disLeft){
            goLeft();
            HAL_Delay(150);
            stop();
        }
    }
    HAL_Delay(50);
}
  • 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

10.小车测速

硬件接线

测速模块:

VCC – 3.3V 不能接5V,否则遮挡一次会触发3次中断

OUT – PB14

unsigned int speedCnt;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_14)
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
speedCnt++;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
printf("speed: %d\r\n", speedCnt);
speedCnt = 0;
}
main函数里:
HAL_TIM_Base_Start_IT(&htim2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

11.串口控制小车并使用Oled显示速度

硬件接线

SCL – PB6

SDA – PB7

封装Oled模块

12.Wi-Fi测速小车并本地Oled显示

硬件接线

把esp8266插进串口1

13.语音控制小车

硬件接线

循迹小车:

循迹模块(左) – PB3

循迹模块(右) – PB4

跟随小车:

跟随模块(左) – PA8

跟随模块(右) – PA9

避障小车:

sg90:PB9

Trig:PA10

Echo:PA11

OLED****模块:

SCL – PB6SDA – PB7

语音模块:

A25 – PA15 (跟随)

A26 – PA13 (避障)

A27 – PA14 (循迹)

上官二号-STM32F1单片机教程_2022版(良许)
    此教程以动手为主,解决大伙学了半天单片机不知道干什么的问题。
    上官二号(小朋友)涉及的内容和知识以小项目为基本单元(暂规划如下)
    小项目做啥就先讲啥,有目标有趣地来一起学习单片机
    喜欢不?
    课程要求:C语言熟练,如果C语言不好,可以学习上官老师录制的C语言课程。另外,最好提前学完
    C51 课程。
    课程特点:不会很正经,不会很学术,不会很理论,不喜勿入!
    一、开发环境的安装
    编程语言:C语言
    需要安装的软件有两个:Keil5 和 STM32CubeMX
    /* 01. 电动车报警器 ====》 IO控制入门 */
    /* 02. 感应开关盖垃圾桶 ====》 定时器,PWM开发,超声波 */
    /* 03. 基于wifi的智能控制插座 =====》 串口开发,ESP8266模块AT控制指令学习,中断学习*/
    /* 04. 基于蓝牙HC-05的智能控制插座 =====》 串口开发,蓝牙穿透*/
    /* 05. 基于4G的智能控制插座 =====》 串口开发,蓝牙穿透*/
    /* 06. 温湿度检测系统 ======》 DS18B20单线协议,如何看时序图,IIC协议液晶屏显示,SPI协议液晶显示
*/
    /* 07. 语音控制开关灯 ======》 语音模块二次开发 */
    /* 08. 智能小车_远程控制/壁障/寻迹/数据采集等 ======》 综合性项目 */
    Keil5 的安装
    使用 Keil4 写 STM32 代码其实也是可以,但需要很复杂的配置,不建议新手操作。
    比较推荐 Keil5 编写 STM32 ,只需要一些简单的设置就可以上手,对新手友好。
    安装
    安装包(不需要太新,本课程以 MDK324 为例,最新的 MDK327 有问题)
    安装过程一路下一步即可(建议不要安装在 C 盘)
    安装路径一定不要有中文或空格!!(重要)
    Keil5 安装完之后,记得安装 F1 固件包
    破姐
    使用
    编程与编译过程与 Keil4 完全一样
    STM32F1 模板工程
    如何下载程序到上官二号
    烧录工具有很多种,比如:串口、J-Link、ST-Link、U-Link 等等,本教程使用 ST-Link。
    安装驱动
    官网下载(慢)https://www.st.com/en/development-tools/stsw-link009.html
资料包
    接线
    配置
    STM32CubeMX 的安装
    作用
    通过界面的方式,快速生成工程文件。
    下载
    官网(慢)https://www.st.com/zh/development-tools/stm32cubemx.html#overview
资料包
    安装
    一路下一步,建议不要安装在C盘
    配置
    更新固件包位置(比较大,默认在C盘,可以更改到其它盘)
    help ---> update settings --> Firmware Repository
    使用STM32CubeMX生成工程文件
    1. 点击「ACCESS TO MCU SELECTOR」;
    2. 左上角搜索对应的芯片,并在右侧双击对应的芯片;
    3. 点击芯片对应的引脚,并进行配置;
    4. 配置工程名称及位置:
    1. 按下图配置 Coder Generator :
    6. 点击右上角 generate code :
7. 点击 Open Project 即可调用 Keil5 打开自动生成的工程文件。
    二、初识STM32单片机
    什么是单片机?
    单片机(Single-Chip Microcomputer)是一种集成电路芯片,把具有数据处理能力的中央处理器
    CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包
    括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个
    小而完善的微型计算机系统,在工业控制领域广泛应用。
    STM系列单片机命名规则
    ST -- 意法半导体
    M -- Microelectronics 微电子
    32 -- 总线宽度
    项目 介绍
    内核 Cortex-M3
    Flash 64K x 8bit
    SRAM 20K x 8bit
    GPIO 37个GPIO,分别为PA0-PA15、PBO-PB15、PC13-PC15、PDO-PD1
    ADC
    2个12bit ADC合计12路通道,外部通道: PAO到PA7+PBO到PB1内部通道: 温度传感器通道
        ADC Channel 16和内部参考电压通道ADC Channel 17
        定时器/
        计数器
        4个16bit定时器/计数器,分别为TIM1、TIM2、TIM3、TIM4TM1带死区插入,常用于产生
        PWM控制电机
        看门狗
        定时器 2个看门狗定时器 (独立看门狗IWDG、窗口看门狗WWDG)
        滴答定
        时器
        1个24bit向下计数的滴答定时器systick
        工作电
        压、温
        度
        2V~3.6V、-40°C~85°C
        通信串
        口
        2 * IIC,2 * SPI,3 * USART,1 * CAN
        STM32F103C8T6单片机简介
        项目 介绍
        系统时
        钟
        内部8MHz时钟HSI最高可倍频到64MHZ,外部8MHZ时钟HSE最高可倍频到72MHZ
        标准库与HAL库区别
        1. 寄存器
        寄存器众多,需要经常翻阅芯片手册,费时费力;
        更大灵活性,可以随心所欲达到自己的目的;
        深入理解单片机的运行原理,知其然更知其所以然。
        2. 标准库
        将寄存器底层操作都封装起来,提供一整套接口(API)供开发者调用
        每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的;
        配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能;
        大大降低单片机开发难度,但是在不同芯片间不方便移植。
        3. HAL库
        ST公司目前主力推的开发方式,新的芯片已经不再提供标准库;
        为了实现在不同芯片之间移植代码;
        为了兼容所有芯片,导致代码量庞大,执行效率低下。
        三、通用输入输出端口GPIO
        什么是GPIO?
        定义
        GPIO是通用输入输出端口的简称,简单来说就是STM32可控制的引脚STM32芯片的GPIO引脚与外部设备连
        接起来,从而实现与外部通讯、控制以及数据采集的功能。
        简单来说我们可以控制GPIO引脚的电平变化,达到我们的各种目的。
        命名规则
        组编号+引脚编号
        组编号:GPIOA, GPIOB, GPIOC, GPIOD .. GPIOG
        引脚编号:0,1,2,3,4...15
        组合起来:
        PA0, PA1, PA2 .. PA15
        PB0, PB1, PB2 .. PB15
        PC0, PC1, PC2 .. PC15
        ...
        有一些特殊功能的引脚是不能用作IO的。
        内部框架图
        下图来源于官方参考手册,了解即可。
        推挽输出与开漏输出
        内部结构图
        推挽输出: 可以真正能真正的输出高电平和低电平
        开漏输出: 开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动
        如何点亮一颗LED灯
        标号一样的导线在物理上是连接在一起的。
        将PB8或PB9拉低,就可以实现将对应的LED灯点亮。
        编程实现点灯
        常用的GPIO HAL库函数:
        结构体 GPIO_InitTypeDef 定义:
        按键点亮LED灯(轮询法)
        输入(按键):
        KEY1:PA0
        KEY2:PA1
        输出(LED灯):
        LED1:PB8
        LED2:PB9
        void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState
                       PinState);
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
typedef struct
{
    uint32_t Pin;
    uint32_t Mode;
    uint32_t Pull;
    uint32_t Speed;
} GPIO_InitTypeDef;
#define KEY_ON 0
#define KEY_OFF 1
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
    if( HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET)
    {
        /* 按键按下 */
        return KEY_ON;
    }
    else
    {
        /* 按键松开 */
        while(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET);
        return KEY_OFF;
    }
}
四、复位和时钟控制(RCC)
    复位
    系统复位
    当发生以下任一事件时,产生一个系统复位:
    1. NRST引脚上的低电平(外部复位)
    2. 窗口看门狗计数终止(WWDG复位)
    3. 独立看门狗计数终止(IWDG复位)
    4. 软件复位(SW复位)
    5. 低功耗管理复位
    电源复位
    当以下事件中之一发生时,产生电源复位:
    1. 上电/掉电复位(POR/PDR复位)
    2. 从待机模式中返回
    备份区复位
    备份区域拥有两个专门的复位,它们只影响备份区域。
    当以下事件中之一发生时,产生备份区域复位。
    1. 软件复位,备份区域复位可由设置备份域控制寄存器 (RCC_BDCR)(见6.3.9节)中的BDRST位
    产生。
    2. 在VDD和VBAT两者掉电的前提下,VDD或VBAT上电将引发备份区域复位。
    时钟控制
    什么是时钟?
    时钟打开,对应的设备才会工作。
    时钟来源
    三种不同的时钟源可被用来驱动系统时钟(SYSCLK)
    HSI振荡器时钟(高速内部时钟)
    HSE振荡器时钟(高速外部时钟)
    while (1)
    {
        /* USER CODE END WHILE */
        if(Key_Scan(GPIOA,GPIO_PIN_0) == KEY_ON)
            HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
        if(Key_Scan(GPIOA,GPIO_PIN_1) == KEY_ON)
            HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
        /* USER CODE BEGIN 3 */
    }
PLL时钟(锁相环倍频时钟)
    二级时钟源:
40kHz低速内部RC(LSIRC)振荡器
    32.768kHz低速外部晶体(LSE晶体)
    如何使用CubeMX配置时钟
    五、中断和事件
    中断概述
    什么是中断?
    中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入
    处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
    什么是EXTI?
    外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测
    器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可
    以单独配置为中断或者事件,以及触发事件的属性。
    EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。
    产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而
    产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。
    EXTI初始化结构体:
    typedef struct
    {
        //中断/事件线
        uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or
disabled.
This parameter can be any combination value
of @ref EXTI_Lines */
        //EXTI 模式
        EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref
EXTIMode_TypeDef */
        //触发类型
        EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge
for the EXTI lines.
This parameter can be a value of @ref
EXTITrigger_TypeDef */
        //EXTI 控制
        FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected
EXTI lines.
This parameter can be set either to ENABLE
or DISABLE */
    }EXTI_InitTypeDef;
中断/事件线:
    #define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
    #define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
    #define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
    #define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
    #define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
    #define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
    #define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
    #define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
    #define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
    #define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
    #define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
    #define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
    #define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
    #define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
    #define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
    #define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
    #define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16
Connected to the PVD Output */
    #define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17
Connected to the RTC Alarm event */
    #define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18
Connected to the USB OTG FS Wakeup from suspend event */
    #define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19
Connected to the Ethernet Wakeup event */
    #define EXTI_Line20 ((uint32_t)0x00100000) /*!< External interrupt line 20
Connected to the USB OTG HS (configured in FS) Wakeup event */
    #define EXTI_Line21 ((uint32_t)0x00200000) /*!< External interrupt line 21
Connected to the RTC Tamper and Time Stamp events */
    #define EXTI_Line22 ((uint32_t)0x00400000) /*!< External interrupt line 22
Connected to the RTC Wakeup event */
    EXTI模式:
    typedef enum
{
    EXTI_Mode_Interrupt = 0x00, //产生中断
    EXTI_Mode_Event = 0x04 //产生事件
}EXTIMode_TypeDef;
触发类型:
    typedef enum
{
    EXTI_Trigger_Rising = 0x08, //上升沿
    EXTI_Trigger_Falling = 0x0C, //下降沿
    EXTI_Trigger_Rising_Falling = 0x10 //上升沿和下降沿都触发
}EXTITrigger_TypeDef;
EXTI控制:
    使能 EXTI ,一般都是使能, ENABLE
    什么是优先级?
    抢占优先级和响应优先级的区别:
    高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
    抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
    抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
    如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行
    什么是优先级分组?
    Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优
    先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:
    第0组:所有4位用于指定响应优先级
    第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
    第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
    第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
    第4组:所有4位用于指定抢占式优先级
    什么是NVIC?
    STM32通过中断控制器NVIC(Nested Vectored Interrupt Controller)进行中断的管理 。NVIC是属于
    Cortex内核的器件,不可屏蔽中断(NMI)和外部中断都由它来处理,但是SYSTICK不是由NVIC控制的。
    什么是中断向量表?
    每个中断源都有对应的处理程序,这个处理程序称为中断服务程序,其入口地址称为中断向量。所有中
    断的中断服务程序入口地址构成一个表,称为中断向量表;也有的机器把中断服务程序入口的跳转指令构成
    一张表,称为中断向量跳转表。
    按键点亮LED灯(中断法)
    1. 配置时钟
    2. 配置GPIO口
    3. 使能中断
    4. 配置工程
    项目一:电动车报警器
    项目需求
    点击遥控器 A 按键,系统进入警戒模式,一旦检测到震动(小偷偷车),则喇叭发出声响报警,吓退小偷。
    点击遥控器 B 按键,系统退出警戒模式,再怎么摇晃系统都不会报警,否则系统一直发出尖叫,让车主尴
    尬。
    项目框图
    typedef struct
    {
        uint8_t NVIC_IRQChannel;
        uint8_t NVIC_IRQChannelPreemptionPriority; //抢断优先级
        uint8_t NVIC_IRQChannelSubPriority; //响应优先级
        FunctionalState NVIC_IRQChannelCmd;
    } NVIC_InitTypeDef;
硬件清单
    振动传感器
    继电器
    高功率喇叭
    433M无线接收发射模块
    杜邦线
    a. 振动传感器介绍及实战
    振动传感器介绍
    单片机供电VCC GND接单片机
    产品不震动,输出高电平,模块上的DO口
    产品震动,输出低电平,绿色指示灯亮
    AO口不用
    编程实现
    需求:当振动传感器接收到振动信号时,使用中断方式点亮LED1。
    如果直接在中断服务函数里调用 HAL_Delay 函数,则会造成系统卡死。
    原因:程序初始化时默认把滴答定时器的中断优先级设为最低,其它中断源很容易打断它导致卡死。
    解决:在 main 函数里使用以下函数提高滴答定时器的中断优先级(提升至0):
    //重写中断服务函数,如果检测到EXTI中断请求,则进入此函数
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    //一根中断线上接有多个中断源,判断中断请求是否来自PA4
    if(GPIO_Pin == GPIO_PIN_4)
    {
        //如果检测到PA4被拉低
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET)
        {
            //则点亮LED1
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
            //延时1秒
            HAL_Delay(1000);
            //关闭LED1
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
        }
        else
        {
            //未检测到PA4被拉低,则关闭LED1
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
        }
    }
}
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
并且将 EXTI4 的中断优先级设置比滴答定时器的中断优先级高,比如 2 。
    b. 继电器介绍及实战
    继电器工作原理
    单片机供电VCC GND接单片机,VCC需要接3.3V,5V不行!
    最大负载电路交流250V/10A,直流30V/10A
    引脚 IN 接收到低电平时,开关闭合。
    编程实现
    c. 433M无线发射接收模块介绍及实战
    433M无线发射接收模块介绍
    单片机供电VCC GND接单片机
    接收到信号,接收模块对应针脚输出高电平
    有D0 D1 D2 D3,对应遥控器的ABCD
    编程实现
    需求:按下遥控器A按键,LED1亮1秒;按下遥控器B按键,LED2亮1秒。
    D0 -- PA5
    D1 -- PA6
    //重写中断服务函数,如果检测到EXTI中断请求,则进入此函数
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    switch(GPIO_Pin)
    {
            // 如果检测到PA5被拉高(按键A被按下)
            项目设计及实现
                项目设计
                //如果检测到PA4被拉低(小偷偷车),并且警报模式打开
                //则将PB7拉低,继电器通电,喇叭一直响
                // 如果检测到PA5被拉高(按键A按下),设定为开启警报模式
                // 则将PB7拉低(喇叭响),2秒后恢复电平(喇叭不响),表示进入警报模式
                // 同时将标志位设置为ON
                // 如果检测到PA6被拉高(按键B按下),设定为关闭警报模式
                // 则将PB7拉低(喇叭响),1秒后恢复电平(喇叭不响),表示关闭警报模式
                // 同时将标志位设置为OFF
                case GPIO_PIN_5:
            if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
            {
                //则点亮LED1
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
                HAL_Delay(1000);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
            }
            else
            {
                //如果未检测到PA5,则关闭LED1
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
            }
            break;
            // 如果检测到PA6被拉高(按键B按下)
        case GPIO_PIN_6:
            if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET)
            {
                //则点亮LED2
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
                HAL_Delay(1000);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
            }
            else
            {
                //如果未检测到PA4,则关闭LED1
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
            }
            break;
    }
}
编程实现
    #define J_ON 1
    #define J_OFF 0
    //重写中断服务函数,如果检测到EXTI中断请求,则进入此函数
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    static int mark = J_OFF;
    switch(GPIO_Pin)
    {
        case GPIO_PIN_4:
            //如果检测到PA4被拉低(小偷偷车),并且警报模式打开
            if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET && mark ==
               J_ON)
            {
                //则将PB7拉低,继电器通电,喇叭一直响
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
            }
            break;
            // 如果检测到PA5被拉高(按键A按下),设定为开启警报模式
        case GPIO_PIN_5:
            if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
            {
                六、定时器Timer
                    定时器介绍
                    软件定时
                    缺点:不精确、占用CPU资源
                    定时器工作原理:
                    使用精准的时基,通过硬件的方式,实现定时功能。定时器核心就是计数器。
                    // 则将PB7拉低(喇叭响),2秒后恢复电平(喇叭不响),表示进入警报模式
                    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
                HAL_Delay(2000);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
                // 同时将标志位设置为ON
                mark = J_ON;
            }
            break;
            // 如果检测到PA6被拉高(按键B按下),设定为关闭警报模式
        case GPIO_PIN_6:
            if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET)
            {
                // 则将PB7拉低(喇叭响),1秒后恢复电平(喇叭不响),表示关闭警报模式
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
                HAL_Delay(1000);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
                // 同时将标志位设置为OFF
                mark = J_OFF;
            }
            break;
    }
}
void Delay500ms() //@11.0592MHz
{
    unsigned char i, j, k;
    _nop_();
    i = 4;
    j = 129;
    k = 119;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}
定时器分类:
    基本定时器(TIM6~TIM7)
    通用定时器(TIM2~TIM5)
    高级定时器(TIM1和TIM8)
    STM32F103C8T6定时器资源:
    通用定时器介绍:
    1) 16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
    2) 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数
    值。
    3)4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
    A.输入捕获
    B.输出比较
    C.PWM 生成(边缘或中间对齐模式)
    D.单脉冲模式输出
    4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电
    路。
    5)如下事件发生时产生中断/DMA:
    A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
    B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
    C.输入捕获
    D.输出比较
    E.支持针对定位的增量(正交)编码器和霍尔传感器电路
    F.触发输入作为外部时钟或者按周期的电流管理
    定时器计数模式:
    定时器时钟源:
    定时器溢出时间计算公式:
    例如,要定时500ms,则:PSC=7199,ARR=4999,Tclk=72M
    定时器中断实验
    需求:使用定时器中断方法,每500ms翻转一次LED1灯状态。
    1. RCC配置
    2. LED1灯配置
    3. 时钟数配置
    4. TIM2配置
    5. 工程配置
    6. 重写更新中断回调函数
    7. 启动定时器
    在main.c中,在定时器初始化命令之后加入以下代码:
    PWM介绍
    STM32F103C8T6 PWM资源:
    高级定时器(TIM1):7路
    通用定时器(TIM2~TIM4):各4路
    PWM输出模式:
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
HAL_TIM_Base_Start_IT(&htim2);
PWM模式1:在向上计数时,一旦 CNT < CCRx 时输出为有效电平,否则为无效电平; 在向下计数
    时,一旦 CNT > CCRx 时输出为无效电平,否则为有效电平。
    PWM模式2:在向上计数时,一旦 CNT < CCRx 时输出为无效电平,否则为有效电平; 在向下计数
    时,一旦 CNT > CCRx 时输出为有效电平,否则为无效电平。
    PWM周期与频率:
    PWM占空比:
    由TIMx_CCRx寄存器决定。
    PWM实验
    需求:使用PWM点亮LED1实现呼吸灯效果。
    LED灯为什么可以越来越亮,越来越暗?
    这是由不同的占空比决定的。
    如何计算周期/频率?
    假如频率为 2kHz ,则:PSC=71,ARR=499
    LED1连接到哪个定时器的哪一路?
    学会看产品手册:
    开始实战!
    1. 设置时钟
    2. 设置定时器
    记得把极性设置为Low,因为LED灯是低电平才亮。
    3. 配置工程
    4. 业务代码
    项目二:感应开关盖垃圾桶
    项目需求
    检测靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
    发生震动时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
    按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
    项目框图
    // 定义变量
    uint16_t pwmVal=0; //调整PWM占空比
uint8_t dir=1; //设置改变方向。1:占空比越来越大;0:占空比越来越小
// 使能 Timer4 第3通道 PWM 输出
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);
// while循环实现呼吸灯效果
while (1)
{
    HAL_Delay(1);
    if (dir)
        pwmVal++;
    else
        pwmVal--;
    if (pwmVal > 500)
        dir = 0;
    if (pwmVal == 0)
        dir =1;
    //修改比较值,修改占空比
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal);
}
硬件清单
    SG90舵机,超声波模块,震动传感器,蜂鸣器
    a. sg90舵机介绍及实战
    sg90舵机介绍
    PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右。
    确定周期/频率
    如果周期为20ms,则 PSC=7199,ARR=199
    角度控制
    0.5ms-------------0度; 2.5% 对应函数中CCRx为5
    1.0ms------------45度; 5.0% 对应函数中CCRx为10
    1.5ms------------90度; 7.5% 对应函数中CCRx为15
    2.0ms-----------135度; 10.0% 对应函数中CCRx为20
    2.5ms-----------180度; 12.5% 对应函数中CCRx为25
    编程实现
    需求:
    每隔1s,转动一个角度:0度 --> 45度 --> 90度 --> 135度 --> 180度 --> 0度
    接线:
    代码:
    b. 超声波传感器介绍及实战
    超声波传感器介绍
    怎么让它发送波
    Trig ,给Trig端口至少10us的高电平
    HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);
while (1)
{
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 5);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 10);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 15);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 20);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 25);
}
怎么知道它开始发了
    Echo信号,由低电平跳转到高电平,表示开始发送波
    怎么知道接收了返回波
    Echo,由高电平跳转回低电平,表示波回来了
    怎么算时间
    Echo引脚维持高电平的时间!
    波发出去的那一下,开始启动定时器
    波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
    怎么算距离
    距离 = 速度 (340m/s)* 时间/2
    编程实战
    需求:
    使用超声波测距,当手离传感器距离小于5cm时,LED1点亮,否则保持不亮状态。
    接线:
    Trig --- PB6
    Echo --- PB7
    LED1 --- PB8
    定时器配置:
    使用 TIM2 ,只用作计数功能,不用作定时。
    将 PSC 配置为71,则计数 1 次代表 1us 。
    编写微秒级函数:
    //使用TIM2来做us级延时函数
    void TIM2_Delay_us(uint16_t n_us)
{
    /* 使能定时器2计数 */
    __HAL_TIM_ENABLE(&htim2);
    __HAL_TIM_SetCounter(&htim2, 0);
    while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
    /* 关闭定时器2计数 */
    __HAL_TIM_DISABLE(&htim2);
}
主函数:
    //1. Trig ,给Trig端口至少10us的高电平
    //2. echo由低电平跳转到高电平,表示开始发送波
    //波发出去的那一下,开始启动定时器
    //3. 由高电平跳转回低电平,表示波回来了
    //波回来的那一下,我们开始停止定时器
    //4. 计算出中间经过多少时间
    //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
    //每500毫秒测试一次距离
    int cnt;
float distance;
while (1)
{
    //1. Trig ,给Trig端口至少10us的高电平
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);//拉高
    TIM2_Delay_us(20);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);//拉低
    //2. echo由低电平跳转到高电平,表示开始发送波
    //波发出去的那一下,开始启动定时器
    while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_RESET);//等待输入电平拉高
    HAL_TIM_Base_Start(&htim2);
    __HAL_TIM_SetCounter(&htim2,0);
    //3. 由高电平跳转回低电平,表示波回来了
    while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET);//等待输入电平变低
    //波回来的那一下,我们开始停止定时器
    HAL_TIM_Base_Stop(&htim2);
    //4. 计算出中间经过多少时间
    cnt = __HAL_TIM_GetCounter(&htim2);
    //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
    distance = cnt*340/2*0.000001*100; //单位:cm
    if(distance < 5)
        项目设计及实现
        项目设计
        超声波模块:
        Trig        --    PB6
        Echo     -- PB7
        sg90舵机:
        PWM -- PB9
        按键:
        KEY1 -- PA0
        LED灯:
        LED1 -- PB8
        震动传感器:
        D0 -- PB5
        VCC -- 5V
        蜂鸣器:
        IO -- PB4
        VCC -- 3V3
        项目实现
        七、串口
        串口介绍
        参见以下视频:
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
    else
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    //每500毫秒测试一次距离
    HAL_Delay(500);
}
常用函数介绍
    串口发送/接收函数:
    HAL_UART_Transmit(); 串口发送数据,使用超时管理机制
    HAL_UART_Receive(); 串口接收数据,使用超时管理机制
    HAL_UART_Transmit_IT(); 串口中断模式发送  
    HAL_UART_Receive_IT(); 串口中断模式接收
    作用:以阻塞的方式发送指定字节的数据
    形参 1 :UART_HandleTypeDef 结构体类型指针变量
    形参 2:指向要发送的数据地址
    形参 3:要发送的数据大小,以字节为单位
    形参 4:设置的超时时间,以ms单位
    作用:以中断的方式接收指定字节的数据
    形参 1 是 UART_HandleTypeDef 结构体类型指针变量
    形参 2 是指向接收数据缓冲区
    形参 3 是要接收的数据大小,以字节为单位
    此函数执行完后将清除中断,需要再次调用以重新开启中断。
    串口中断回调函数:
    HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //发送中断回调函数
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //接收中断回调函数
状态标记变量:
    USART_RX_STA
    HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart,
                                        uint8_t *pData, uint16_t Size, uint32_t Timeout)
    HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart,
                                          uint8_t *pData, uint16_t Size)
    从0开始,串口中断接收到一个数据(一个字节)就自增1。当数据读取全部OK时候(回车和换行符号来的时
    候),那么 USART_RX_STA的最高位置1,表示串口数据接收全部完毕了,然后main函数里面可以处理数据
    了。
    串口接收中断流程
    串口实验(非中断)
    需求:
    接受串口工具发送的字符串,并将其发送回串口工具。
    硬件接线:
    TX -- A10
    RX -- A9
    一定要记得交叉接线!!
    串口配置:
    1. 选定串口
    2. 选择模式
    异步通讯
    3. 串口配置
    4. 使用MicroLIB库
    从魔术棒打开,这个勾勾一定要打上,否则 printf 无法重映射!
    编程实现:
    串口实验(中断)
    需求:
    通过中断的方法接受串口工具发送的字符串,并将其发送回串口工具。
    #include <stdio.h>
    #include <string.h>
    unsigned char ch[20] = {0};
int fputc(int ch, FILE *f)
{
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);
    return ch;
}
main函数里:
    unsigned char ch[20] = {0};
HAL_UART_Transmit(&huart1, "hello world\n", strlen("hello world\n"), 100);
while(1)
{
    HAL_UART_Receive(&huart1, ch, 19, 100);
    //HAL_UART_Transmit(&huart1, ch, strlen(ch), 100);
    printf(ch);
    memset(ch, 0, strlen(ch));
}
硬件接线:
    同上
    串口配置:
    前4步同上
    5. 打开中断
    编程实现:
    #include <stdio.h>
    //串口接收缓存(1字节)
    uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    // 判断中断是由哪个串口触发的
    if(huart->Instance == USART1)
    {
        // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
        if((UART1_RX_STA & 0x8000) == 0)
        {
            // 如果已经收到了 0x0d (回车),
            if(UART1_RX_STA & 0x4000)
            {
                // 则接着判断是否收到 0x0a (换行)
                if(buf == 0x0a)
                    // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
                    UART1_RX_STA |= 0x8000;
                else
                    // 否则认为接收错误,重新开始
                    UART1_RX_STA = 0;
            }
            else // 如果没有收到了 0x0d (回车)
            {
                //则先判断收到的这个字符是否是 0x0d (回车)
                if(buf == 0x0d)
                {
                    // 是的话则将 bit14 位置为1
                    UART1_RX_STA |= 0x4000;
                }
                else
                {
                    // 否则将接收到的数据保存在缓存数组里
                    UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
                    UART1_RX_STA++;
                    // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
                    if(UART1_RX_STA > UART1_REC_LEN - 1)
                        UART1_RX_STA = 0;
                }
            }
        }
        // 重新开启中断
        HAL_UART_Receive_IT(&huart1, &buf, 1);
    }
}
int fputc(int ch, FILE *f)
{
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);
    return ch;
}
main函数部分
    HAL_UART_Receive_IT(&huart1, &buf, 1);
while (1)
{
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    //判断判断串口是否接收完成
    if(UART1_RX_STA & 0x8000)
    {
        printf("收到数据:");
        // 将收到的数据发送到串口
        HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff);
        // 等待发送完成
        项目三:蓝牙插座风扇灯
            项目需求
            通过蓝牙模块,实现手机控制蓝牙插座/风扇/灯。
            本质:
            1. 采用蓝牙的透传功能;
            2. 控制 IO 口的输出。
            项目框图
            硬件清单
            HC01蓝牙模块
            CH340
            杜邦线
            项目设计及实现
            while(huart1.gState != HAL_UART_STATE_READY);
        printf("\r\n");
        // 重新开始下一次接收
        UART1_RX_STA = 0;
    }
    printf("hello liangxu\r\n");
    HAL_Delay(1000);
}
项目设计
    HC01_TX -- RX1
    HC01_RX -- TX1
    项目实现
    1. 串口非中断法
    2. 串口中断法
    项目四:Wi-Fi插座风扇灯
    项目需求
    通过ESP8266模块,实现手机控制wifi插座/风扇/灯。
    项目框图
    HAL_UART_Receive(&huart1, ch, 19, 100);
//HAL_UART_Transmit(&huart1, ch, strlen(ch), 100);
//printf((char *)ch);
printf("%s", ch);
if (!strcmp((const char *)ch, "open")) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET)
        printf("LED1已打开\n");
}else if(!strcmp((const char *)ch, "close")) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET)
        printf("LED1已关闭\n");
} else {
    if(ch[0] != '\0')
        printf("指令发送错误:%s", ch);
}
printf("收到数据:");
if (!strcmp((const char *)UART1_RX_Buffer, "open")) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET)
        printf("LED1已打开\n");
}else if(!strcmp((const char *)UART1_RX_Buffer, "close")) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET)
        printf("LED1已关闭\n");
} else {
    if(UART1_RX_Buffer[0] != '\0')
        printf("指令发送错误:%s", UART1_RX_Buffer);
}
硬件清单
    ESP8266模块
    CH340
    杜邦线
    项目设计及实现
    项目设计
    串口1用于与ESP8266通讯,串口2连接PC,用于打印log,查看系统状态。
    项目实现
    注意:
    1. 工作中一般不直接在中断服务函数里处理数据,而是在收到数据后直接丢给队列,再处理数据;
    2. 在中断服务函数里尽量减少使用延时函数及打印函数。
    AP模式:
    #define SIZE 12
    char buffer[SIZE];
char LJWL[] = "AT+CWJAP=\"TP-LINK_3E30\",\"18650711783\"\r\n"; //入网指令
char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.0.130\",8880\r\n"; //连接服务器指令
char TCMS[] = "AT+CIPMODE=1\r\n"; //透传指令
char SJCS[] = "AT+CIPSEND\r\n"; //数据传输开始指令
char CQMK[] = "AT+RST\r\n"; //重启模块指令
char AT_OK_Flag = 0; //OK返回值的标志位
char AT_Connect_Net_Flag = 0; //WIFI GOT IP返回值的标志位
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    // 判断中断是由哪个串口触发的
    if(huart->Instance == USART1)
    {
        // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
        if((UART1_RX_STA & 0x8000) == 0)
        {
            // 如果已经收到了 0x0d (回车),
            if(UART1_RX_STA & 0x4000)
            {
                // 则接着判断是否收到 0x0a (换行)
                if(buf == 0x0a)
                {
                    // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
                    UART1_RX_STA |= 0x8000;
                    // 查看是否收到 WIFI GOT IP
                    if(!strcmp((uint8_t *)UART1_RX_Buffer, "WIFI GOT IP"))
                        AT_Connect_Net_Flag = 1;
                    // 查看是否收到 OK
                    if(!strcmp((uint8_t *)UART1_RX_Buffer, "OK"))
                        AT_OK_Flag = 1;
                    // 查看是否收到 FAIL
                    if(!strcmp((uint8_t *)UART1_RX_Buffer, "FAIL"))
                    {
                        int i = 0;
                        for(i = 0; i < 5; i++)
                        {
                            HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
                            HAL_Delay(1000);
                        }
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
                        printf(CQMK);
                    }
                    // 灯控指令
                    if(!strcmp((uint8_t *)UART1_RX_Buffer, "L-1"))
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
                    if(!strcmp((uint8_t *)UART1_RX_Buffer, "L-0"))
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
                    memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
                    UART1_RX_STA = 0;
                }
                else
                    // 否则认为接收错误,重新开始
                    UART1_RX_STA = 0;
            }
            else // 如果没有收到了 0x0d (回车)
            {
                //则先判断收到的这个字符是否是 0x0d (回车)
                if(buf == 0x0d)
                {
                    // 是的话则将 bit14 位置为1
                    UART1_RX_STA |= 0x4000;
                }
                else
                {
                    // 否则将接收到的数据保存在缓存数组里
                    UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
                    UART1_RX_STA++;
                    // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
                    if(UART1_RX_STA > UART1_REC_LEN - 1)
                        UART1_RX_STA = 0;
                }
            }
        }
        // 重新开启中断
        HAL_UART_Receive_IT(&huart1, &buf, 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_USART1_UART_Init();
    MX_USART2_UART_Init();
    /* USER CODE BEGIN 2 */
    HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
    // 开启接收中断
    HAL_UART_Receive_IT(&huart1, &buf, 1);
    HAL_UART_Transmit(&huart2, "let's go!!\r\n", strlen("let's go!!\r\n"), 100);
    //发送联网AT指令并等待成功
    printf(LJWL);
    //while(!AT_Connect_Net_Flag);
    while(!AT_OK_Flag) HAL_Delay(50);
    AT_OK_Flag = 0;
    //发送连服务器指令并等待成功
    printf(LJFWQ);
    while(!AT_OK_Flag) HAL_Delay(50);
    AT_OK_Flag = 0;
    //发送透传模式指令并等待成功
    printf(TCMS);
    while(!AT_OK_Flag) HAL_Delay(50);
    AT_OK_Flag = 0;
    //发送数据传输指令并等待成功
    printf(SJCS);
    while(!AT_OK_Flag) HAL_Delay(50);
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
        printf("liangxu shuai\r\n");
        HAL_UART_Transmit(&huart2, "hello liangxu\r\n", strlen("hello liangxu\r\n"),
                          100);
        HAL_Delay(3000);
    }
    /* USER CODE END 3 */
}
STA模式:
    #include <stdio.h>
    #include <string.h>
    char buffer[SIZE];
//1 工作在路由模式
char LYMO[] = "AT+CWMODE=2\r\n";
//2 使能多链接
char DLJ[] = "AT+CIPMUX=1\r\n";
//3 建立TCPServer
char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333
//发送数据
char FSSJ[] = "AT+CIPSEND=0,5\r\n";
char AT_OK_Flag = 0; //OK返回值的标志位
char AT_Connect_Net_Flag = 0; //WIFI GOT IP返回值的标志位
char Client_Connect_Flag = 0;
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    // 判断中断是由哪个串口触发的
    if(huart->Instance == USART1)
    {
        // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
        if((UART1_RX_STA & 0x8000) == 0)
        {
            // 如果已经收到了 0x0d (回车),
            if(UART1_RX_STA & 0x4000)
            {
                // 则接着判断是否收到 0x0a (换行)
                if(buf == 0x0a)
                {
                    // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
                    UART1_RX_STA |= 0x8000;
                    // 查看是否收到 WIFI GOT IP
                    if(!strcmp(UART1_RX_Buffer, "WIFI GOT IP"))
                        AT_Connect_Net_Flag = 1;
                    // 查看是否收到 OK
                    if(!strcmp(UART1_RX_Buffer, "OK"))
                        AT_OK_Flag = 1;
                    // 查看是否收到 FAIL
                    if(!strcmp(UART1_RX_Buffer, "0,CONNECT"))
                        Client_Connect_Flag = 1;
                    // 灯控指令
                    if(!strcmp(UART1_RX_Buffer, "L-1"))
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
                    if(!strcmp(UART1_RX_Buffer, "L-0"))
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
                    memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
                    UART1_RX_STA = 0;
                }
                else
                    // 否则认为接收错误,重新开始
                    UART1_RX_STA = 0;
            }
            else // 如果没有收到了 0x0d (回车)
            {
                //则先判断收到的这个字符是否是 0x0d (回车)
                if(buf == 0x0d)
                {
                    // 是的话则将 bit14 位置为1
                    UART1_RX_STA |= 0x4000;
                }
                else
                {
                    // 否则将接收到的数据保存在缓存数组里
                    UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
                    UART1_RX_STA++;
                    // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
                    if(UART1_RX_STA > UART1_REC_LEN - 1)
                        UART1_RX_STA = 0;
                }
            }
        }
        // 重新开启中断
        HAL_UART_Receive_IT(&huart1, &buf, 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();
    项目五:4G遥控插座风扇灯
        /* USER CODE BEGIN SysInit */
        /* USER CODE END SysInit */
        /* Initialize all configured peripherals */
        MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    /* USER CODE BEGIN 2 */
    HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
    // 开启接收中断
    HAL_UART_Receive_IT(&huart1, &buf, 1);
    HAL_UART_Transmit(&huart2, "let's go\r\n", strlen("let's go\r\n"), 100);
    printf(LYMO);
    while(!AT_OK_Flag) HAL_Delay(50);
    AT_OK_Flag = 0;
    printf(DLJ);
    while(!AT_OK_Flag) HAL_Delay(50);
    AT_OK_Flag = 0;
    printf(JLFW);
    while(!Client_Connect_Flag) HAL_Delay(50);
    AT_OK_Flag = 0;
    if(Client_Connect_Flag){
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    }
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
        //4 发送数据
        printf(FSSJ);
        HAL_Delay(2000);
        printf("Hello");
        HAL_Delay(2000);
    }
    /* USER CODE END 3 */
}
项目需求
    通过4G模块,实现电脑控制插座/风扇/灯。
    项目框图
    注意:
    由于硬件的限制,上官二号无法直接带动 4G 模块,可以将 4G 模块的 VCC 和 GND 插到 CH340 的 5V 和
    GND 里。
    硬件清单
    4G模块
    CH340
    杜邦线
    项目设计及实现
    项目设计
    1. 服务器搭建
    参照C51课程;
    2. 代码修改
    其实可以直接复用上节课的代码,把不相关的代码删除即可
    项目实现
    八、独立看门狗 IWDG
    // 按视频删除不相关代码即可
    独立看门狗介绍
    什么是看门狗?
    在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑
    飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停
    滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监
    测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。
    独立看门狗工作在主程序之外,能够完全独立工作,它的时钟是专用的低速时钟(LSI),由 VDD 电压供
    电, 在停止模式和待机模式下仍能工作。
    独立看门狗本质
    本质是一个 12 位的递减计数器,当计数器的值从某个值一直减到0的时候,系统就会产生一个复位信号,即
    IWDG_RESET 。
    如果在计数没减到0之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的
    喂狗。
    独立看门狗框图
    独立看门狗时钟
    独立看门狗的时钟由独立的RC振荡器LSI提供,即使主时钟发生故障它仍然有效,非常独立。启用IWDG后,
    LSI时钟会自动开启。LSI时钟频率并不精确,F1用40kHz。
    LSI经过一个8位的预分频器得到计数器时钟。
    分频系数算法:
    prer是IWDG_PR 的值。
    重装载寄存器
    重装载寄存器是一个12位的寄存器,用于存放重装载值,低12位有效,即最大值为4096,这个值的大小决
    定着独立看门狗的溢出时间。
    键寄存器
    键寄存器IWDG_KR可以说是独立看门狗的一个控制寄存器,主要有三种控制方式,往这个寄存器写入下面三
    个不同的值有不同的效果。
    溢出时间计算公式
    独立看门狗实验
    需求:
    开启独立看门狗,溢出时间为1秒,使用按键1进行喂狗。
    硬件接线:
    KEY1 -- PA0
    UART1 -- PA9/PA10
    溢出时间计算:
    PSC=64,RLR=625
    编程实现:
    九、窗口看门狗 WWDG
    窗口看门狗介绍
    什么是窗口看门狗?
    窗口看门狗用于监测单片机程序运行时效是否精准,主要检测软件异常,一般用于需要精准检测程序运行时
    间的场合。
    窗口看门狗的本质是一个能产生系统复位信号和提前唤醒中断的6位计数器。
    产生复位条件:
    当递减计数器值从 0x40 减到 0x3F 时复位(即T6位跳变到0)
    计数器的值大于 W[6:0] 值时喂狗会复位。
    产生中断条件:
    当递减计数器等于 0x40 时可产生提前唤醒中断 (EWI)。
    在窗口期内重装载计数器的值,防止复位,也就是所谓的喂狗。
    #include <string.h>
    main函数:
    HAL_UART_Transmit(&huart1, "程序启动。。\n", strlen("程序启动。。\n"), 100);
while (1)
{
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
        HAL_IWDG_Refresh(&hiwdg);
    HAL_Delay(50);
}
窗口看门狗工作原理
    WWDG框图
    控制寄存器
    配置寄存器
    状态寄存器
    超时时间计算
    Tout是WWDG超时时间(没喂狗)
    Fwwdg是WWDG的时钟源频率(最大36M)
    4096是WWDG固定的预分频系数
    2^WDGTB是WWDG_CFR寄存器设置的预分频系数值
    T[5:0]是WWDG计数器低6位,最多63
    窗口看门狗实验
    需求:
    开启窗口看门狗,计数器值设置为 0X7F ,窗口值设置为 0X5F ,预分频系数为 8 。程序启动时点亮 LED1 ,
    300ms 后熄灭。在提前唤醒中断服务函数进行喂狗,同时翻转 LED2 状态。
    硬件接线:
    LED1 -- PB8
    LED2 -- PB9
    WWDG配置:
    编程实现:
    void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
    HAL_WWDG_Refresh(hwwdg);
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
}
main函数
    MX_GPIO_Init();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(300);
MX_WWDG_Init();
while (1)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    HAL_Delay(40);
    对比点 独立看门狗 窗口看门狗
        时钟源 独立时钟,LSI (40KHz) ,不精确 PCLK1或PCLK3,精确
        复位条件 递减计数到0 窗口期外喂狗或减到0x3F
        中断 没有中断 计数值减到0x40可产生中断
        递减计数器位数 12位(最大计数范围:4096~0) 7位(最大计数范围:127~63)
        应用场合 防止程序跑飞,死循环,死机 检测程序时效,防止软件异常
        独立看门狗和窗口看门狗的异同点
        十、DMA
        DMA介绍
        什么是DMA?
        令人头秃的描述:
        DMA(Direct Memory Access,直接存储器访问) 提供在外设与内存、存储器和存储器、外设与外设之
        间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,
        CPU对于内存的工作来说就无法使用。
        简单描述:
        就是一个数据搬运工!!
        DMA的意义
        代替 CPU 搬运数据,为 CPU 减负。
        1. 数据搬运的工作比较耗时间;
        2. 数据搬运工作时效要求高(有数据来就要搬走);
        3. 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。
}
搬运什么数据?
    存储器、外设
    这里的外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设,而这里的存储器包括
    自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的。
    三种搬运方式:
    存储器→存储器(例如:复制某特别大的数据buf)
    存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
    外设→存储器 (例如:将串口RDR寄存器写入某数据buf)
    存储器→存储器
    存储器→外设
    外设→存储器
    DMA 控制器
    STM32F103有2个 DMA 控制器,DMA1有7个通道,DMA2有5个通道。
    一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。
    DMA1有7个通道:
    DMA2有5个通道
    DMA及通道的优先级
    优先级管理采用软件+硬件:
    软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级
    最高级>高级>中级>低级
    硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。
    比如:如果软件优先级相同,通道2优先于通道4
    DMA传输方式
    DMA_Mode_Normal(正常模式) 
    一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
    DMA_Mode_Circular(循环传输模式) 
    当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输
    模式
    指针递增模式
    外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址
    将是前一个地址加上增量值。
    实验一、内存到内存搬运
    实验要求
    使用DMA的方式将数组A的内容复制到数组B中,搬运完之后将数组B的内容打印到屏幕。
    CubeMX配置
    DMA 配置:
    重定向 printf 的话记得将下面这个勾打开:
    用到的库函数
    1. HAL_DMA_Start
    参数一:DMA_HandleTypeDef *hdma,DMA通道句柄
    参数二:uint32_t SrcAddress,源内存地址
    参数三:uint32_t DstAddress,目标内存地址
    参数四:uint32_t DataLength,传输数据长度。注意:需要乘以sizeof(uint32_t)
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress,
                                    uint32_t DstAddress, uint32_t DataLength)
    2. __HAL_DMA_GET_FLAG
    参数一:HANDLE,DMA通道句柄
    参数二:FLAG,数据传输标志。DMA_FLAG_TCx表示数据传输完成标志
    返回值:FLAG的值(SET/RESET)
    代码实现
    1. 开启数据传输
    2. 等待数据传输完成
    3. 打印数组内容
    实验二、内存到外设搬运
    #define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))
    #define BUF_SIZE 16
    // 源数组
    uint32_t srcBuf[BUF_SIZE] = {
    0x00000000,0x11111111,0x22222222,0x33333333,
    0x44444444,0x55555555,0x66666666,0x77777777,
    0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
    0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
};
// 目标数组
uint32_t desBuf[BUF_SIZE];
int fputc(int ch, FILE *f)
{
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);
    return ch;
}
main函数里:
    // 开启数据传输
    HAL_DMA_Start(&hdma_memtomem_dma1_channel1,
                  (uint32_t)srcBuf, (uint32_t)desBuf, sizeof(uint32_t) * BUF_SIZE);
// 等待数据传输完成
while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);
// 打印数组内容
for (i = 0; i < BUF_SIZE; i++)
    printf("Buf[%d] = %X\r\n", i, desBuf[i]);
实验要求
    使用DMA的方式将内存数据搬运到串口1发送寄存器,同时闪烁LED1。
    CubeMX配置
    DMA配置
    用到的库函数
    HAL_UART_Transmit_DMA
    参数一:UART_HandleTypeDef *huart,串口句柄
    参数二:uint8_t *pData,待发送数据首地址
    参数三:uint16_t Size,待发送数据长度
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData,
                                            uint16_t Size)
    代码实现
    1. 准备数据
    2. 将数据通过串口DMA发送
    实验三、外设到内存搬运
    实验要求
    使用DMA的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁LED1。
    CubeMX配置
    DMA配置:
    #define BUF_SIZE 1000
    // 待发送的数据
    unsigned char sendBuf[BUF_SIZE];
main函数里
    // 准备数据
    for (i = 0; i < BUF_SIZE; i++)
        sendBuf[i] = 'A';
// 将数据通过串口DMA发送
HAL_UART_Transmit_DMA(&huart1, sendBuf, BUF_SIZE);
while (1)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
    HAL_Delay(100);
}
串口中断配置
    用到的库函数
    1. __HAL_UART_ENABLE
    #define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U)
    == UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) &
                                                             UART_IT_MASK)): \
    (((__INTERRUPT__) >> 28U)
     == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) &
                                                              UART_IT_MASK)): \
    ((__HANDLE__)->Instance-
     >CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
    参数一:HANDLE,串口句柄
    参数二:INTERRUPT,需要使能的中断
    返回值:无
    2. HAL_UART_Receive_DMA
    HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData,
                                           uint16_t Size)
    参数一:UART_HandleTypeDef *huart,串口句柄
    参数二:uint8_t *pData,接收缓存首地址
    参数三:uint16_t Size,接收缓存长度
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    3. __HAL_UART_GET_FLAG
    #define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &
    (__FLAG__)) == (__FLAG__))
    参数一:HANDLE,串口句柄
    参数二:FLAG,需要查看的FLAG
    返回值:FLAG的值
    4. __HAL_UART_CLEAR_IDLEFLAG
    #define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
    参数一:HANDLE,串口句柄
    返回值:无
    5. HAL_UART_DMAStop
    HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
    参数一:UART_HandleTypeDef *huart,串口句柄
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    6. __HAL_DMA_GET_COUNTER
    #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
    参数一:HANDLE,串口句柄
    返回值:未传输数据大小
    代码实现
    如何判断串口接收是否完成?如何知道串口收到数据的长度?
    使用串口空闲中断(IDLE)!
    串口空闲时,触发空闲中断;
    空闲中断标志位由硬件置1,软件清零
    利用串口空闲中断,可以用如下流程实现DMA控制的任意长数据接收:
    1. 使能IDLE空闲中断;
    2. 使能DMA接收中断;
    3. 收到串口接收中断,DMA不断传输数据到缓冲区;
    4. 一帧数据接收完毕,串口暂时空闲,触发串口空闲中断;
    5. 在中断服务函数中,清除中断标志位,关闭DMA传输(防止干扰);
    6. 计算刚才收到了多少个字节的数据。
    7. 处理缓冲区数据,开启DMA传输,开始下一帧接收。
    有三个文件需要修改:
    main.c
    main.h
    stm32f1xx_it.c
    uint8_t rcvBuf[BUF_SIZE]; // 接收数据缓存数组
uint8_t rcvLen = 0; // 接收一帧数据的长度
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能IDLE空闲中断
HAL_UART_Receive_DMA(&huart1,rcvBuf,100); // 使能DMA接收中断
while (1)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
    HAL_Delay(300);
}
#define BUF_SIZE 100
extern uint8_t rcvBuf[BUF_SIZE];
extern uint8_t rcvLen;
void USART1_IRQHandler(void)
{
    /* USER CODE BEGIN USART1_IRQn 0 */
    /* USER CODE END USART1_IRQn 0 */
    十一、ADC
        ADC介绍
        ADC是什么?
        全称:Analog-to-Digital Converter,指模拟/数字转换器
        ADC的性能指标
        量程:能测量的电压范围
        分辨率:ADC能辨别的最小模拟量,通常以输出二进制数的位数表示,比如:8、10、12、16位等;位
        数越多,分辨率越高,一般来说分辨率越高,转化时间越长
        转化时间:从转换开始到获得稳定的数字量输出所需要的时间称为转换时间
        ADC特性
        12位精度下转换速度可高达1MHZ
        供电电压:V SSA :0V,V DDA :2.4V~3.6V
        ADC输入范围:VREF- ≤ VIN ≤ VREF+
        采样时间可配置,采样时间越长, 转换结果相对越准确, 但是转换速度就越慢
        ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中
        HAL_UART_IRQHandler(&huart1);
    /* USER CODE BEGIN USART1_IRQn 1 */
    if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET)) // 判断IDLE标志位是否被置位
    {
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除标志位
        HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止干扰
        uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
        rcvLen = BUF_SIZE - temp; //计算数据长度
        HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);//发送数据
        HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);//开启DMA
    }
    /* USER CODE END USART1_IRQn 1 */
}
ADC通道
    总共2个ADC(ADC1,ADC2),每个ADC有18个转换通道: 16个外部通道、 2个内部通道(温度传感器、内
        部参考电压)。
        外部的16个通道在转换时又分为规则通道和注入通道,其中规则通道最多有16路,注入通道最多有4路。
        规则组:正常排队的人;
        注入组:有特权的人(军人、孕妇)
        ADC转换顺序
        每个ADC只有一个数据寄存器,16个通道一起共用这个寄存器,所以需要指定规则转换通道的转换顺序。
        规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄存器控制
        着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通道就是第x个转换。
        和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器
        来控制,控制关系如下:
        注入序列的转换顺序是从JSQx[ 4 : 0 ](x=4-JL[1:0])开始。只有当JL=4的时候,注入通道的转换顺序才会按
        照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。
        ADC触发方式
        1. 通过向控制寄存器ADC-CR2的ADON位写1来开启转换,写0停止转换。
        2. 也可以通过外部事件(如定时器)进行转换。
        ADC转化时间
        ADC是挂载在APB2总线(PCLK2)上的,经过分频器得到ADC时钟(ADCCLK),最高 14 MHz。
        12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,
        采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us。
        转换时间=采样时间+12.5个周期
        ADC转化模式
        扫描模式
        关闭扫描模式:只转换ADC_SQRx或ADC_JSQR选中的第一个通道
        打开扫描模式:扫描所有被ADC_SQRx或ADC_JSQR选中的所有通道
        单次转换/连续转换
        单次转换:只转换一次
        连续转换:转换一次之后,立马进行下一次转换
        实验:使用ADC读取烟雾传感器的值
        CubeMX配置
        代码实现
        十二、IIC
        IIC介绍
        笔记参照:上官一号笔记第5章节;
        视频参照:上官一号92~103节
        函数封装
        用到的库函数:
        参数一:I2C_HandleTypeDef *hi2c,I2C设备句柄
        参数二:uint16_t DevAddress,目标器件的地址,七位地址必须左对齐
        参数三:uint16_t MemAddress,目标器件的目标寄存器地址
        参数四:uint16_t MemAddSize,目标器件内部寄存器地址数据长度
        while (1)
        {
            HAL_ADC_Start(&hadc1); //启动ADC单次转换
            HAL_ADC_PollForConversion(&hadc1, 50); //等待ADC转换完成
            smoke_value = HAL_ADC_GetValue(&hadc1); //读取ADC转换数据
            printf("smoke_value = %f\r\n", 3.3/4096 * smoke_value);
            //printf("smoke_value = %d \r\n", smoke_value);
            HAL_Delay(500);
        }
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)
    参数五:uint8_t *pData,待写的数据首地址
    参数六:uint16_t Size,待写的数据长度
    参数七:uint32_t Timeout,超时时间
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    向OLED写命令的封装:
    向OLED写数据的封装:
    重做上官一号的IIC实验
    接线:
    SCL -- PB6
    SDA -- PB7
    void Oled_Write_Cmd(uint8_t dataCmd)
{
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
                      &dataCmd, 1, 0xff);
}
void Oled_Write_Data(uint8_t dataData)
{
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
                      &dataData, 1, 0xff);
}
void Oled_Write_Cmd(uint8_t dataCmd)
{
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
                      &dataCmd, 1, 0xff);
}
void Oled_Write_Data(uint8_t dataData)
{
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
                      &dataData, 1, 0xff);
}
void Oled_Init(void){
    Oled_Write_Cmd(0xAE);//--display off
    Oled_Write_Cmd(0x00);//---set low column address
    Oled_Write_Cmd(0x10);//---set high column address
    Oled_Write_Cmd(0x40);//--set start line address
    Oled_Write_Cmd(0xB0);//--set page address
    Oled_Write_Cmd(0x81); // contract control
    Oled_Write_Cmd(0xFF);//--128
    Oled_Write_Cmd(0xA1);//set segment remap
    Oled_Write_Cmd(0xA6);//--normal / reverse
    Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
    Oled_Write_Cmd(0x3F);//--1/32 duty
    Oled_Write_Cmd(0xC8);//Com scan direction
    Oled_Write_Cmd(0xD3);//-set display offset
    Oled_Write_Cmd(0x00);//
    Oled_Write_Cmd(0xD5);//set osc division
    Oled_Write_Cmd(0x80);//
    Oled_Write_Cmd(0xD8);//set area color mode off
    Oled_Write_Cmd(0x05);//
    Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
    Oled_Write_Cmd(0xF1);//
    Oled_Write_Cmd(0xDA);//set com pin configuartion
    Oled_Write_Cmd(0x12);//
    Oled_Write_Cmd(0xDB);//set Vcomh
    Oled_Write_Cmd(0x30);//
    Oled_Write_Cmd(0x8D);//set charge pump enable
    Oled_Write_Cmd(0x14);//
    Oled_Write_Cmd(0xAF);//--turn on oled panel
}
void Oled_Screen_Clear(void){
    int i,n;
    Oled_Write_Cmd (0x20); //set memory addressing mode
    Oled_Write_Cmd (0x02); //page addressing mode
    for(i=0;i<8;i++){
        Oled_Write_Cmd(0xb0+i); //éè??ò3μ??·£¨0~7£?
        Oled_Write_Cmd(0x00); //éè????ê??????aáDμíμ??·
        Oled_Write_Cmd(0x10); //éè????ê??????aáD??μ??·
        for(n=0;n<128;n++)Oled_Write_Data(0x00);
    }
}
unsigned char bmpImager[] = {
    /*-- 调入了一幅图像:D:\无标题.bmp --*/
    /*-- 宽度x高度=128x64 --128x8x8*/
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0xF0,0x08,0x0C,0x04,0x06,0x06,0x0C,0x04,0x0C,0xFC,0x1C,0x74,0xFC,0xF8,
    0xF0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x01,0x07,0x04,0x88,0xF8,0x08,0x08,0x0C,0x06,0x01,0x00,0x00,0x01,0x1F,
    0x7F,0xFF,0xDC,0xF8,0xE0,0xC0,0x40,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x10,0x18,0x08,0x0C,
    0x04,0x04,0x06,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x60,0xC0,0x80,0x80,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
    0x03,0x06,0x1C,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x07,0xFC,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x88,0xE8,0x38,0x0E,0x09,0x08,
    0x08,0x88,0xE8,0x18,0x08,0x08,0x08,0x00,0x00,0xFF,0x89,0x89,0x89,0xFF,0x00,0xFF,
    0x89,0x89,0x89,0x89,0xFF,0x00,0x00,0x04,0x04,0x84,0x74,0x6F,0xA4,0x24,0x24,0x24,
    0x24,0xA4,0x64,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x80,0xF0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x7F,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x08,0x09,0x09,0x06,0x06,
    0x06,0x05,0x08,0x08,0x10,0x10,0x00,0x00,0x0C,0x03,0x10,0x10,0x10,0x1F,0x18,0x07,
    0x00,0x00,0x10,0x10,0x1F,0x00,0x10,0x08,0x06,0x11,0x10,0x08,0x09,0x0A,0x06,0x06,
    0x0B,0x08,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,
    0x1E,0x03,0x00,0x00,0xC0,0x60,0x30,0x0C,0x04,0x06,0x02,0x01,0x01,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
    0x1E,0x60,0x78,0x0F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void Oled_Show_Image(unsigned char *image)
{
    unsigned char i;
    unsigned int j;
    for(i=0;i<8;i++){
        Oled_Write_Cmd(0xB0 + i);//page0--page7
        //每个page从0列
        Oled_Write_Cmd(0x00);
        Oled_Write_Cmd(0x10);
        //0到127列,依次写入0,每写入数据,列地址自动偏移
        for(j = 128 * i; j<(128 * (i+1));j++){
            Oled_Write_Data(image[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_I2C1_Init();
    /* USER CODE BEGIN 2 */
    //1. OLED初始化
    十三、SPI
        SPI 介绍
        SPI 是什么?
        SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且
        在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这
        种简单易用的特性,越来越多的芯片集成了这种通信协议,比如 AT91RM9200 。
        以上介绍来自360百科
        SPI 物理架构
        Oled_Init();
    //2. 选择一个位置
    //2.1 确认页寻址模式
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
    Oled_Show_Image(bmpImager);
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}
SPI 包含 4 条总线,SPI 总线包含 4 条总线,分别为SS、SCK、MOSI、MISO。它们的作用介绍如下 :
    (1) MISO – Master Input Slave Output,主设备数据输入,从设备数据输出
    (2) MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入
    (3) SCK – Serial Clock,时钟信号,由主设备产生
    (4) CS – Chip Select,片选信号,由主设备控制
    SPI 工作原理
    SPI 工作模式
    时钟极性(CPOL):
    没有数据传输时时钟线的空闲状态电平
    0:SCK在空闲状态保持低电平
    1:SCK在空闲状态保持高电平
    时钟相位(CPHA):
    时钟线在第几个时钟边沿采样数据
    0:SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存
    1:SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁存
    模式 0 和模式 3 最常用。
    模式 0 时序图:
    模式 3 时序图:
    W25Q128 介绍
    什么是 W25Q128 ?
    W25Q128 是华邦公司推出的一款 SPI 接口的 NOR Flash 芯片,其存储空间为 128 Mbit,相当于 16M 字
    节。
    Flash 是常用的用于储存数据的半导体器件,它具有容量大,可重复擦写、按“扇区/块”擦除、掉电后数据可
    继续保存的特性。
    Flash 是有一个物理特性:只能写 0 ,不能写 1 ,写 1 靠擦除。
    指令(HEX) 名称 作用
    0X06 写使能 写入数据/擦除之前,必须先发送该指令
    0X05 读SR1 判定FLASH是否处于空闲状态,擦除用
    0X03 读数据 用于读取NOR FLASH数据
    0X02 页写 用于写入NOR FLASH数据,最多写256字节
    0X20 扇区擦除 扇区擦除指令,最小擦除单位(4096字节)
    W25Q128 存储架构
    一般按扇区(4k)进行擦除。
    可以按 章 -- 节 -- 页 -- 字 进行理解。
    W25Q128 常用指令
    W25Q128 全部指令非常多,但常用的如下几个指令:
    写使能 (06H)
    执行页写,扇区擦除,块擦除,片擦除,写状态寄存器等指令前,需要写使能。
    拉低CS片选 → 发送06H → 拉高CS片选
    读状态寄存器(05H)
    拉低CS片选 → 发送05H→ 返回SR1的值 → 拉高CS片选
    读时序(03H)
    拉低CS片选 → 发送03H→ 发送24位地址 → 读取数据(1~n) → 拉高CS片选
    页写时序 (02H)
    页写命令最多可以向FLASH传输256个字节的数据。
    拉低CS片选 → 发送02H→ 发送24位地址 → 发送数据(1~n) → 拉高CS片选
    扇区擦除时序(20H)
    写入数据前,检查内存空间是否全部都是 0XFF ,不满足需擦除。
    拉低CS片选 → 发送20H→ 发送24位地址 → 拉高CS片选
    W25Q128 状态寄存器
    W25Q128 一共有 3 个状态寄存器,它们的作用是跟踪芯片的状态。
    其中,状态寄存器 1 较为常用。
    BUSY:指示当前的状态,0 表示空闲,1 表示忙碌
    WEL:写使能锁定,为 1 时,可以操作页/扇区/块。为 0 时,写禁止。
    W25Q128 常见操作流程
    以下流程省略了拉低/拉高片选信号CS。
    读操作:
    擦除扇区:
    写操作
    实验:使用 SPI 通讯读写 W25Q128 模块
    硬件接线
    VCC -- 3.3V
    CS -- PA4
    CLK -- PA5
    DO -- PA6
    DI -- PA7
    cubeMX配置
    w25q128_write_nocheck流程图
    项目六、温湿度LCD显示并上传服务器
    项目需求
    使用温湿度传感器模块(DHT11)获取温度及湿度,并将值显示在LCD1602上,同时通过蓝牙模块透传到手
    机。
    项目框图
    硬件清单
    DHT11
    LCD1602
    HC-08
    继电器
    杜邦线
    a. LCD1602介绍及实战
    硬件接线
    D0~D7 -- A0~A7
    RS -- B1
    RW -- B2
    EN -- B10
    V0 -- GND(正视看不到显示结果,需要侧着看。否则需要接可调电阻)
    引脚封装
    RS、RW、EN三根信号线经常需要进行拉高/拉低操作,可以进行封装
    如何将一个字节的数据按位一次性发送到GPIOA的8个管脚?
    #define RS_GPIO_Port GPIOB
    #define RW_GPIO_Port GPIOB
    #define EN_GPIO_Port GPIOB
    #define RS_Pin GPIO_PIN_1
    #define RW_Pin GPIO_PIN_2
    #define EN_Pin GPIO_PIN_10
    #define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_SET)
    #define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_RESET)
    #define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_SET)
    #define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_RESET)
    #define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET)
    #define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET)
    GPIOA->ODR = cmd;
代码实现
    #define RS_GPIO_Port GPIOB
    #define RW_GPIO_Port GPIOB
    #define EN_GPIO_Port GPIOB
    #define RS_Pin GPIO_PIN_1
    #define RW_Pin GPIO_PIN_2
    #define EN_Pin GPIO_PIN_10
    #define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_SET)
    #define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_RESET)
    #define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_SET)
    #define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_RESET)
    #define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET)
    #define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET)
    void Write_Cmd_Func(uint8_t cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;
    HAL_Delay(5);
    EN_HIGH;
    HAL_Delay(5);
    EN_LOW;
}
void Write_Data_Func(uint8_t dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;
    HAL_Delay(5);
    EN_HIGH;
    HAL_Delay(5);
    EN_LOW;
}
void LCD1602_INIT(void)
{
    //(1)延时 15ms
    HAL_Delay(15);
    //(2)写指令 38H(不检测忙信号)
    Write_Cmd_Func(0x38);
    //(3)延时 5ms
    HAL_Delay(5);
    //(4)以后每次写指令,读/写数据操作均需要检测忙信号
    //(5)写指令 38H:显示模式设置
    Write_Cmd_Func(0x38);
    b. DHT11介绍及实战
        //(6)写指令 08H:显示关闭
        Write_Cmd_Func(0x08);
    //(7)写指令 01H:显示清屏
    Write_Cmd_Func(0x01);
    //(8)写指令 06H:显示光标移动设置
    Write_Cmd_Func(0x06);
    //(9)写指令 0CH:显示开及光标设置}
    Write_Cmd_Func(0x0c);
}
void LCD1602_showLine(char row, char col, char *string)
{
    switch(row){
        case 1:
            Write_Cmd_Func(0x80+col);
            while(*string){
                Write_Data_Func(*string);
                string++;
            }
            break;
        case 2:
            Write_Cmd_Func(0x80+0x40+col);
            while(*string){
                Write_Data_Func(*string);
                string++;
            }
            break;
    }
}
main函数里:
    //char position = 0x80 + 0x05;
    //char dataShow = 'C';
    LCD1602_INIT();
//Write_Cmd_Func(position);//选择要显示的地址
//Write_Data_Func(dataShow);//发送要显示的字符
LCD1602_showLine(1,5,"NO.2");
LCD1602_showLine(2,0,"LX handsome");
硬件接线
    DAT -- PB7
    注意:PB7既作为输入,也作为输出,则不能直接在CubeMX里配置,需要自己写代码
    引脚封装
    代码实现
    #define DHT_HIGHT HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
    #define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)
    #define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)
    #define DHT_HIGHT HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
    #define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)
    #define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)
    uint8_t datas[5];
void delay_us(uint16_t cnt)
{
    uint8_t i;
    while(cnt)
    {
        for (i = 0; i < 10; i++)
        {
        }
        cnt--;
    }
}
void DHT_GPIO_Init(uint32_t Mode)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = Mode;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void DHT11_Start(void)
{
    DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);
    DHT_HIGHT;
    DHT_LOW;
    HAL_Delay(30);
    DHT_HIGHT;
    DHT_GPIO_Init(GPIO_MODE_INPUT);
    while(DHT_VALUE);
    while(!DHT_VALUE);
    while(DHT_VALUE);
}
void Read_Data_From_DHT()
{
    int i;//轮
    int j;//每一轮读多少次
    char tmp;
    char flag;
    DHT11_Start();
    DHT_GPIO_Init(GPIO_MODE_INPUT);
    for(i= 0;i < 5;i++){
        for(j=0;j<8;j++){
            while(!DHT_VALUE);//等待卡g点
            delay_us(40);
            if(DHT_VALUE == 1){
                flag = 1;
                while(DHT_VALUE);
            }else{
                flag = 0;
            }
            tmp = tmp << 1;
            tmp |= flag;
        }
        datas[i] = tmp;
    }
}
int fputc(int ch, FILE *f)
{
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);
    return ch;
}
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();
    项目设计及实现
        项目设计
        继电器数据线插在PB6上,DHT11及LCD1602接线与上述相同。
        项目实现
        注意点:
        1. 不要忘记将Use MicroLIB的勾打上;
        2. 不要忘记在main函数把串口中断打开;
        3. 使用蓝牙模块时,记得将波特率设置为9600.
        /* 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();
    /* USER CODE BEGIN 2 */
    printf("hello world\r\n");
    HAL_Delay(2000);
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
        //DHT_Get_Temp_Humi_Data(datas);
        Read_Data_From_DHT();
        printf("Temp: %d.%d ", datas[2], datas[3]);
        printf("Humi: %d.%d\r\n", datas[0], datas[1]);
        HAL_Delay(2000);
    }
    /* USER CODE END 3 */
}
项目七:智能小车
    1. 让小车动起来
    对应源代码:smartCar_project1
    硬件接线
    B-1A -- PB0
    B-1B -- PB1
    A-1A -- PB2
    A-1B -- PB10
    其余接线参考上官一号小车项目。
    代码实现
    motor.c
    char message[16];
while (1)
{
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    Read_Data_From_DHT();
    if (datas[2] >= 25)
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
    else
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    memset(message, 0, sizeof(message));
    sprintf(message, "Temp: %d.%d", datas[2], datas[3]);
    LCD1602_showLine(1,0,message);
    memset(message, 0, sizeof(message));
    sprintf(message, "Humi: %d.%d", datas[0], datas[1]);
    LCD1602_showLine(2,0,message);
    HAL_Delay(1000);
}
#include "motor.h"
void goForward(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goBack(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
}
void goLeft(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goRight(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void stop(void)
{
    // 左轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    // 右轮
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
motor.h
    main.c
    2. 串口控制小车
    对应源代码:smartCar_project2
    代码实现
    usart.c
    #ifndef __MOTOR_H__
    #define __MOTOR_H__
    #include "main.h"
    void goForward(void);
void goBack(void);
void goLeft(void);
void goRight(void);
void stop(void);
#endif
#include "motor.h"
//main函数的while循环部分:
while (1)
{
    /* USER CODE END WHILE */
    goForward();
    HAL_Delay(1000);
    goBack();
    HAL_Delay(1000);
    goLeft();
    HAL_Delay(1000);
    goRight();
    HAL_Delay(1000);
    stop();
    HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
}
#include "string.h"
#include "stdio.h"
#include "motor.h"
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
#define SIZE 12
char buffer[SIZE];
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    // 判断中断是由哪个串口触发的
    if(huart->Instance == USART1)
    {
        // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
        if((UART1_RX_STA & 0x8000) == 0)
        {
            // 如果已经收到了 0x0d (回车),
            if(UART1_RX_STA & 0x4000)
            {
                // 则接着判断是否收到 0x0a (换行)
                if(buf == 0x0a)
                {
                    // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
                    UART1_RX_STA |= 0x8000;
                    // 灯控指令
                    if(!strcmp(UART1_RX_Buffer, "M1"))
                        goForward();
                    else if(!strcmp(UART1_RX_Buffer, "M2"))
                        goBack();
                    else if(!strcmp(UART1_RX_Buffer, "M3"))
                        goLeft();
                    else if(!strcmp(UART1_RX_Buffer, "M4"))
                        goRight();
                    else
                        stop();
                    memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
                    UART1_RX_STA = 0;
                }
                else
                    // 否则认为接收错误,重新开始
                    UART1_RX_STA = 0;
                main.c
                    3. 点动控制小车
                    对应源代码:smartCar_project3
            }
            else // 如果没有收到了 0x0d (回车)
            {
                //则先判断收到的这个字符是否是 0x0d (回车)
                if(buf == 0x0d)
                {
                    // 是的话则将 bit14 位置为1
                    UART1_RX_STA |= 0x4000;
                }
                else
                {
                    // 否则将接收到的数据保存在缓存数组里
                    UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
                    UART1_RX_STA++;
                    // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
                    if(UART1_RX_STA > UART1_REC_LEN - 1)
                        UART1_RX_STA = 0;
                }
            }
        }
        // 重新开启中断
        HAL_UART_Receive_IT(&huart1, &buf, 1);
    }
}
int fputc(int ch, FILE *f)
{
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);
    return ch;
}
#include "motor.h"
extern uint8_t buf;
//main函数
HAL_UART_Receive_IT(&huart1, &buf, 1);
代码实现
    usart.c
    main.c
    4. 硬件PWM调速
    对应源代码:smartCar_project4
    硬件接线
    B-1A -- PA0
    B-1B -- PB1
    A-1A -- PA1
    A-1B -- PB10
    其余接线参考上官一号小车项目。
    if (!strcmp(UART1_RX_Buffer, "M1"))
    {
        goForward();
        HAL_Delay(10);
    }
else if (!strcmp(UART1_RX_Buffer, "M2"))
{
    goBack();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M3"))
{
    goLeft();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M4"))
{
    goRight();
    HAL_Delay(10);
}
else
    stop();
// main函数里
HAL_NVIC_SetPriority(SysTick_IRQn,0,0); //或者通过cubeMX配置
while(1)
{
    stop();
}
cubeMX配置
    TIM2配置如下图。
    这两节里配置有误,正确的应该是PSC=7199,ARR=199,大家注意修正!!
    设置 PSC=71 ,ARR=19,PWM 周期则为 20ms 。
    将控制车轮的4个 GPIO 口配置修改如下,否则小车动不起来。
    原因:L9110每个控制口需要一高一低才可以动起来,如果PWM有效电平为高电平,则另一个GPIO口则需
    要输出低电平才可以驱动轮子。
    代码实现
    main.c
    5. 左右轮各自调速
    对应源代码:smartCar_project5
    代码实现
    main.c
    6. 循迹小车
    对应源代码:smartCar_project6
    硬件接线
    B-1A -- PB0
    B-1B -- PB1
    A-1A -- PB2
    // main函数里
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
while (1)
{
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 8);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 8);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 10);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 10);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 15);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 15);
    HAL_Delay(1000);
}
// main函数里
while (1)
{
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
    HAL_Delay(1000);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
    HAL_Delay(1000);
}
A-1B -- PB10
    循迹模块(左) -- PB3
    循迹模块(右) -- PB4
    其余接线参考上官一号小车项目。
    代码实现
    7. 循迹小车解决转弯平滑问题
    对应源代码:smartCar_project7
    硬件接线
    B-1A -- PA0
    B-1B -- PB1
    A-1A -- PA1
    A-1B -- PB10
    循迹模块(左) -- PB3
    循迹模块(右) -- PB4
    其余接线参考上官一号小车项目。
    代码实现
    #define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
    #define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
    // main函数里
    while (1)
    {
        if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
            goForward();
        if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
            goLeft();
        if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
            goRight();
        if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
            stop();
    }
#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
// main函数里
while (1)
    8. 跟随小车
    对应源代码:smartCar_project8
    硬件接线
    B-1A -- PB0
    B-1B -- PB1
    A-1A -- PB2
    A-1B -- PB10
    跟随模块(左) -- PB5
    跟随模块(右) -- PB6
    其余接线参考上官一号小车项目。
    代码实现
{
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,19);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,19);
    }
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);
    }
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);
    }
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
    {
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,0);
        __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,0);
    }
}
#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6)
// main函数里
while (1)
    9. 摇头避障小车
    对应源代码:smartCar_project9
    9.1 封装摇头功能
    对应源代码:smartCar_project9_1
    硬件接线
    sg90 -- PB9
    cubeMX配置
{
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)
        goForward();
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)
        goRight();
    if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)
        goLeft();
    if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)
        stop();
}
代码实现
    sg90.c
    #include "sg90.h"
    #include "gpio.h"
    #include "tim.h"
    void initSG90(void)
{
    HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}
void sgMiddle(void)
{
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}
void sgRight(void)
    sg90.h
    main.c
    9.2 封装超声波传感器
    对应源代码:smartCar_project9_2
    硬件接线
    请注意,超声波模块的接线与垃圾桶项目有所不同!
    超声波模块:
    Trig       --    PB7
    Echo     -- PB8
{
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
}
void sgLeft(void)
{
    __HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); //将舵机置为180度
}
#ifndef __SG90_H__
#define __SG90_H__
void initSG90(void);
void sgMiddle(void);
void sgRight(void);
void sgLeft(void);
#endif
initSG90();
HAL_Delay(1000);
while (1)
{
    sgLeft();
    HAL_Delay(1000);
    sgMiddle();
    HAL_Delay(1000);
    sgRight();
    HAL_Delay(1000);
    sgMiddle();
    HAL_Delay(1000);
}
cubeMX配置
    代码实现
    sr04.c
    #include "sr04.h"
    #include "gpio.h"
    #include "tim.h"
    //使用TIM2来做us级延时函数
    void TIM2_Delay_us(uint16_t n_us)
{
    /* 使能定时器2计数 */
    __HAL_TIM_ENABLE(&htim2);
    __HAL_TIM_SetCounter(&htim2, 0);
    while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
    /* 关闭定时器2计数 */
    __HAL_TIM_DISABLE(&htim2);
}
double get_distance(void)
{
    int cnt=0;
    //1. Trig ,给Trig端口至少10us的高电平
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);//拉高
    TIM2_Delay_us(20);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);//拉低
    //2. echo由低电平跳转到高电平,表示开始发送波
    //波发出去的那一下,开始启动定时器
    while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET);//等待输入电平拉高
    HAL_TIM_Base_Start(&htim2);
    __HAL_TIM_SetCounter(&htim2,0);
    //3. 由高电平跳转回低电平,表示波回来了
    while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET);//等待输入电平变低
    //波回来的那一下,我们开始停止定时器
    HAL_TIM_Base_Stop(&htim2);
    //4. 计算出中间经过多少时间
    cnt = __HAL_TIM_GetCounter(&htim2);
    //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
    return (cnt*340/2*0.000001*100); //单位:cm
}
sr04.h
    #ifndef __SR04_H__
    #define __SR04_H__
    double get_distance(void);
#endif
main.c
    while (1)
    {
        if(dir != MIDDLE){
            sgMiddle();
            dir = MIDDLE;
            HAL_Delay(300);
            9.3 封装电机驱动
                对应源代码:smartCar_project9_3
                硬件接线
                与 “让小车动起来” 完全一样
                B-1A -- PB0
                B-1B -- PB1
                A-1A -- PB2
                A-1B -- PB10
                代码实现
        }
        disMiddle = get_distance();
        if(disMiddle > 35){
            //前进
        }
        else
        {
            //停止
            //测左边距离
            sgLeft();
            HAL_Delay(300);
            disLeft = get_distance();
            sgMiddle();
            HAL_Delay(300);
            sgRight();
            dir = RIGHT;
            HAL_Delay(300);
            disRight = get_distance();
        }
    }
while (1)
{
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    if(dir != MIDDLE){
        sgMiddle();
        dir = MIDDLE;
        HAL_Delay(300);
        10. 小车测速
            对应源代码:smartCar_project10
            硬件接线
            测速模块:
            VCC -- 3.3V 不能接5V,否则遮挡一次会触发3次中断
            OUT -- PB14
    }
    disMiddle = get_distance();
    if(disMiddle > 35){
        //前进
        goForward();
    }else if(disMiddle < 10){
        goBack();
    }else
    {
        //停止
        stop();
        //测左边距离
        sgLeft();
        HAL_Delay(300);
        disLeft = get_distance();
        sgMiddle();
        HAL_Delay(300);
        sgRight();
        dir = RIGHT;
        HAL_Delay(300);
        disRight = get_distance();
        if(disLeft < disRight){
            goRight();
            HAL_Delay(150);
            stop();
        }
        if(disRight < disLeft){
            goLeft();
            HAL_Delay(150);
            stop();
        }
    }
    HAL_Delay(50);
}
cubeMX配置
    代码实现
    11.  串口控制小车并使用Oled显示速度
    对应源代码:smartCar_project11
    unsigned int speedCnt;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_14)
        if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
            speedCnt++;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    printf("speed: %d\r\n", speedCnt);
    speedCnt = 0;
}
main函数里:
    HAL_TIM_Base_Start_IT(&htim2);
硬件接线
    SCL -- PB6
    SDA -- PB7
    封装Oled模块
    对应源代码:smartCar_project11_1
    实现测速并使用Oled显示速度
    对应源代码:smartCar_project11_2
    代码实现
    太长了,大家直接去看源代码吧。。
    12. Wi-Fi测速小车并本地Oled显示
    对应源代码:smartCar_project12
    硬件接线
    把esp8266插进串口1
    13. 语音控制小车
    对应源代码:smartCar_project13
    硬件接线
    循迹小车:
    循迹模块(左) -- PB3
    循迹模块(右) -- PB4
    跟随小车:
    跟随模块(左) -- PA8
    跟随模块(右) -- PA9
    避障小车:
    sg90:PB9
    Trig:PA10
    Echo:PA11
    OLED模块:
    SCL -- PB6
    SDA -- PB7
    语音模块:
    A25 -- PA15 (跟随)
    A26 -- PA13 (避障)
    A27 -- PA14 (循迹)
    cubeMX配置
    SU-03T配置
    配置与上官一号一样样~
    注意事项
    1. 语音模块电源需要接到上官二号,否则小车跟随功能异常;
    2. 上官二号烧录代码时,需要将语音模块断电,否则烧录失败。
    代码实现
    #include "sg90.h"
    #include "sr04.h"
    #include "motor.h"
    #include "oled.h"
    #include "string.h"
    #define MIDDLE 0
    #define LEFT 1
    #define RIGHT 2
    #define BZ 1
    #define XJ 2
    #define GS 3
    #define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
    #define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
    #define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
    #define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)
    #define XJ_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14)
    #define GS_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
    #define BZ_VALUE HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13)
    char dir;
void xunjiMode()
{
    if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ ==
       GPIO_PIN_RESET)
        goForward();
    if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET)
        goLeft();
    if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET)
        goRight();
    if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET)
        stop();
}
void gensuiMode()
{
    if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS ==
       GPIO_PIN_RESET)
        goForward();
    if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET)
        goRight();
    if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET)
        goLeft();
    if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET)
        stop();
}
void bizhangMode()
{
    double disMiddle;
    double disLeft;
    double disRight;
    if(dir != MIDDLE){
        sgMiddle();
        dir = MIDDLE;
        HAL_Delay(300);
    }
    disMiddle = get_distance();
    if(disMiddle > 35){
        //前进
        goForward();
    }else if(disMiddle < 10){
        goBack();
    }else
    {
        //停止
        stop();
        //测左边距离
        sgLeft();
        HAL_Delay(300);
        disLeft = get_distance();
        sgMiddle();
        HAL_Delay(300);
        sgRight();
        dir = RIGHT;
        HAL_Delay(300);
        disRight = get_distance();
        if(disLeft < disRight){
            goRight();
            HAL_Delay(150);
            stop();
        }
        if(disRight < disLeft){
            goLeft();
            HAL_Delay(150);
            stop();
        }
    }
    HAL_Delay(50);
}
int main(void)
{
    int mark = 0;
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM4_Init();
    MX_TIM2_Init();
    MX_I2C1_Init();
    /* USER CODE BEGIN 2 */
    initSG90();
    HAL_Delay(1000);
    dir = MIDDLE;
    Oled_Init();
    Oled_Screen_Clear();
    Oled_Show_Str(2,2,"-----Ready----");
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
        //满足循迹模式的条件
        if(XJ_VALUE == GPIO_PIN_RESET && GS_VALUE == GPIO_PIN_SET && BZ_VALUE ==
           GPIO_PIN_SET)
        {
            if(mark != XJ)
            {
                Oled_Screen_Clear();
                Oled_Show_Str(2,2,"-----XunJi----");
            }
            mark = XJ;
            xunjiMode();
        }
        //满足跟随模式的条件
        if(XJ_VALUE == GPIO_PIN_SET && GS_VALUE == GPIO_PIN_RESET && BZ_VALUE ==
           GPIO_PIN_SET)
        {
            if(mark != GS)
            {
                Oled_Screen_Clear();
                Oled_Show_Str(2,2,"-----GenSui----");
            }
            mark = GS;
            gensuiMode();
        }
        //满足避障模式的条件
        if(XJ_VALUE == GPIO_PIN_SET && GS_VALUE == GPIO_PIN_SET && BZ_VALUE ==
           GPIO_PIN_RESET)
        {
            if(mark != BZ)
            {
                Oled_Screen_Clear();
                Oled_Show_Str(2,2,"-----BiZhang----");
            }
            mark = BZ;
            bizhangMode();
        }
        HAL_Delay(50);
    }
    /* USER CODE END 3 */
}
  • 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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731
  • 732
  • 733
  • 734
  • 735
  • 736
  • 737
  • 738
  • 739
  • 740
  • 741
  • 742
  • 743
  • 744
  • 745
  • 746
  • 747
  • 748
  • 749
  • 750
  • 751
  • 752
  • 753
  • 754
  • 755
  • 756
  • 757
  • 758
  • 759
  • 760
  • 761
  • 762
  • 763
  • 764
  • 765
  • 766
  • 767
  • 768
  • 769
  • 770
  • 771
  • 772
  • 773
  • 774
  • 775
  • 776
  • 777
  • 778
  • 779
  • 780
  • 781
  • 782
  • 783
  • 784
  • 785
  • 786
  • 787
  • 788
  • 789
  • 790
  • 791
  • 792
  • 793
  • 794
  • 795
  • 796
  • 797
  • 798
  • 799
  • 800
  • 801
  • 802
  • 803
  • 804
  • 805
  • 806
  • 807
  • 808
  • 809
  • 810
  • 811
  • 812
  • 813
  • 814
  • 815
  • 816
  • 817
  • 818
  • 819
  • 820
  • 821
  • 822
  • 823
  • 824
  • 825
  • 826
  • 827
  • 828
  • 829
  • 830
  • 831
  • 832
  • 833
  • 834
  • 835
  • 836
  • 837
  • 838
  • 839
  • 840
  • 841
  • 842
  • 843
  • 844
  • 845
  • 846
  • 847
  • 848
  • 849
  • 850
  • 851
  • 852
  • 853
  • 854
  • 855
  • 856
  • 857
  • 858
  • 859
  • 860
  • 861
  • 862
  • 863
  • 864
  • 865
  • 866
  • 867
  • 868
  • 869
  • 870
  • 871
  • 872
  • 873
  • 874
  • 875
  • 876
  • 877
  • 878
  • 879
  • 880
  • 881
  • 882
  • 883
  • 884
  • 885
  • 886
  • 887
  • 888
  • 889
  • 890
  • 891
  • 892
  • 893
  • 894
  • 895
  • 896
  • 897
  • 898
  • 899
  • 900
  • 901
  • 902
  • 903
  • 904
  • 905
  • 906
  • 907
  • 908
  • 909
  • 910
  • 911
  • 912
  • 913
  • 914
  • 915
  • 916
  • 917
  • 918
  • 919
  • 920
  • 921
  • 922
  • 923
  • 924
  • 925
  • 926
  • 927
  • 928
  • 929
  • 930
  • 931
  • 932
  • 933
  • 934
  • 935
  • 936
  • 937
  • 938
  • 939
  • 940
  • 941
  • 942
  • 943
  • 944
  • 945
  • 946
  • 947
  • 948
  • 949
  • 950
  • 951
  • 952
  • 953
  • 954
  • 955
  • 956
  • 957
  • 958
  • 959
  • 960
  • 961
  • 962
  • 963
  • 964
  • 965
  • 966
  • 967
  • 968
  • 969
  • 970
  • 971
  • 972
  • 973
  • 974
  • 975
  • 976
  • 977
  • 978
  • 979
  • 980
  • 981
  • 982
  • 983
  • 984
  • 985
  • 986
  • 987
  • 988
  • 989
  • 990
  • 991
  • 992
  • 993
  • 994
  • 995
  • 996
  • 997
  • 998
  • 999
  • 1000
  • 1001
  • 1002
  • 1003
  • 1004
  • 1005
  • 1006
  • 1007
  • 1008
  • 1009
  • 1010
  • 1011
  • 1012
  • 1013
  • 1014
  • 1015
  • 1016
  • 1017
  • 1018
  • 1019
  • 1020
  • 1021
  • 1022
  • 1023
  • 1024
  • 1025
  • 1026
  • 1027
  • 1028
  • 1029
  • 1030
  • 1031
  • 1032
  • 1033
  • 1034
  • 1035
  • 1036
  • 1037
  • 1038
  • 1039
  • 1040
  • 1041
  • 1042
  • 1043
  • 1044
  • 1045
  • 1046
  • 1047
  • 1048
  • 1049
  • 1050
  • 1051
  • 1052
  • 1053
  • 1054
  • 1055
  • 1056
  • 1057
  • 1058
  • 1059
  • 1060
  • 1061
  • 1062
  • 1063
  • 1064
  • 1065
  • 1066
  • 1067
  • 1068
  • 1069
  • 1070
  • 1071
  • 1072
  • 1073
  • 1074
  • 1075
  • 1076
  • 1077
  • 1078
  • 1079
  • 1080
  • 1081
  • 1082
  • 1083
  • 1084
  • 1085
  • 1086
  • 1087
  • 1088
  • 1089
  • 1090
  • 1091
  • 1092
  • 1093
  • 1094
  • 1095
  • 1096
  • 1097
  • 1098
  • 1099
  • 1100
  • 1101
  • 1102
  • 1103
  • 1104
  • 1105
  • 1106
  • 1107
  • 1108
  • 1109
  • 1110
  • 1111
  • 1112
  • 1113
  • 1114
  • 1115
  • 1116
  • 1117
  • 1118
  • 1119
  • 1120
  • 1121
  • 1122
  • 1123
  • 1124
  • 1125
  • 1126
  • 1127
  • 1128
  • 1129
  • 1130
  • 1131
  • 1132
  • 1133
  • 1134
  • 1135
  • 1136
  • 1137
  • 1138
  • 1139
  • 1140
  • 1141
  • 1142
  • 1143
  • 1144
  • 1145
  • 1146
  • 1147
  • 1148
  • 1149
  • 1150
  • 1151
  • 1152
  • 1153
  • 1154
  • 1155
  • 1156
  • 1157
  • 1158
  • 1159
  • 1160
  • 1161
  • 1162
  • 1163
  • 1164
  • 1165
  • 1166
  • 1167
  • 1168
  • 1169
  • 1170
  • 1171
  • 1172
  • 1173
  • 1174
  • 1175
  • 1176
  • 1177
  • 1178
  • 1179
  • 1180
  • 1181
  • 1182
  • 1183
  • 1184
  • 1185
  • 1186
  • 1187
  • 1188
  • 1189
  • 1190
  • 1191
  • 1192
  • 1193
  • 1194
  • 1195
  • 1196
  • 1197
  • 1198
  • 1199
  • 1200
  • 1201
  • 1202
  • 1203
  • 1204
  • 1205
  • 1206
  • 1207
  • 1208
  • 1209
  • 1210
  • 1211
  • 1212
  • 1213
  • 1214
  • 1215
  • 1216
  • 1217
  • 1218
  • 1219
  • 1220
  • 1221
  • 1222
  • 1223
  • 1224
  • 1225
  • 1226
  • 1227
  • 1228
  • 1229
  • 1230
  • 1231
  • 1232
  • 1233
  • 1234
  • 1235
  • 1236
  • 1237
  • 1238
  • 1239
  • 1240
  • 1241
  • 1242
  • 1243
  • 1244
  • 1245
  • 1246
  • 1247
  • 1248
  • 1249
  • 1250
  • 1251
  • 1252
  • 1253
  • 1254
  • 1255
  • 1256
  • 1257
  • 1258
  • 1259
  • 1260
  • 1261
  • 1262
  • 1263
  • 1264
  • 1265
  • 1266
  • 1267
  • 1268
  • 1269
  • 1270
  • 1271
  • 1272
  • 1273
  • 1274
  • 1275
  • 1276
  • 1277
  • 1278
  • 1279
  • 1280
  • 1281
  • 1282
  • 1283
  • 1284
  • 1285
  • 1286
  • 1287
  • 1288
  • 1289
  • 1290
  • 1291
  • 1292
  • 1293
  • 1294
  • 1295
  • 1296
  • 1297
  • 1298
  • 1299
  • 1300
  • 1301
  • 1302
  • 1303
  • 1304
  • 1305
  • 1306
  • 1307
  • 1308
  • 1309
  • 1310
  • 1311
  • 1312
  • 1313
  • 1314
  • 1315
  • 1316
  • 1317
  • 1318
  • 1319
  • 1320
  • 1321
  • 1322
  • 1323
  • 1324
  • 1325
  • 1326
  • 1327
  • 1328
  • 1329
  • 1330
  • 1331
  • 1332
  • 1333
  • 1334
  • 1335
  • 1336
  • 1337
  • 1338
  • 1339
  • 1340
  • 1341
  • 1342
  • 1343
  • 1344
  • 1345
  • 1346
  • 1347
  • 1348
  • 1349
  • 1350
  • 1351
  • 1352
  • 1353
  • 1354
  • 1355
  • 1356
  • 1357
  • 1358
  • 1359
  • 1360
  • 1361
  • 1362
  • 1363
  • 1364
  • 1365
  • 1366
  • 1367
  • 1368
  • 1369
  • 1370
  • 1371
  • 1372
  • 1373
  • 1374
  • 1375
  • 1376
  • 1377
  • 1378
  • 1379
  • 1380
  • 1381
  • 1382
  • 1383
  • 1384
  • 1385
  • 1386
  • 1387
  • 1388
  • 1389
  • 1390
  • 1391
  • 1392
  • 1393
  • 1394
  • 1395
  • 1396
  • 1397
  • 1398
  • 1399
  • 1400
  • 1401
  • 1402
  • 1403
  • 1404
  • 1405
  • 1406
  • 1407
  • 1408
  • 1409
  • 1410
  • 1411
  • 1412
  • 1413
  • 1414
  • 1415
  • 1416
  • 1417
  • 1418
  • 1419
  • 1420
  • 1421
  • 1422
  • 1423
  • 1424
  • 1425
  • 1426
  • 1427
  • 1428
  • 1429
  • 1430
  • 1431
  • 1432
  • 1433
  • 1434
  • 1435
  • 1436
  • 1437
  • 1438
  • 1439
  • 1440
  • 1441
  • 1442
  • 1443
  • 1444
  • 1445
  • 1446
  • 1447
  • 1448
  • 1449
  • 1450
  • 1451
  • 1452
  • 1453
  • 1454
  • 1455
  • 1456
  • 1457
  • 1458
  • 1459
  • 1460
  • 1461
  • 1462
  • 1463
  • 1464
  • 1465
  • 1466
  • 1467
  • 1468
  • 1469
  • 1470
  • 1471
  • 1472
  • 1473
  • 1474
  • 1475
  • 1476
  • 1477
  • 1478
  • 1479
  • 1480
  • 1481
  • 1482
  • 1483
  • 1484
  • 1485
  • 1486
  • 1487
  • 1488
  • 1489
  • 1490
  • 1491
  • 1492
  • 1493
  • 1494
  • 1495
  • 1496
  • 1497
  • 1498
  • 1499
  • 1500
  • 1501
  • 1502
  • 1503
  • 1504
  • 1505
  • 1506
  • 1507
  • 1508
  • 1509
  • 1510
  • 1511
  • 1512
  • 1513
  • 1514
  • 1515
  • 1516
  • 1517
  • 1518
  • 1519
  • 1520
  • 1521
  • 1522
  • 1523
  • 1524
  • 1525
  • 1526
  • 1527
  • 1528
  • 1529
  • 1530
  • 1531
  • 1532
  • 1533
  • 1534
  • 1535
  • 1536
  • 1537
  • 1538
  • 1539
  • 1540
  • 1541
  • 1542
  • 1543
  • 1544
  • 1545
  • 1546
  • 1547
  • 1548
  • 1549
  • 1550
  • 1551
  • 1552
  • 1553
  • 1554
  • 1555
  • 1556
  • 1557
  • 1558
  • 1559
  • 1560
  • 1561
  • 1562
  • 1563
  • 1564
  • 1565
  • 1566
  • 1567
  • 1568
  • 1569
  • 1570
  • 1571
  • 1572
  • 1573
  • 1574
  • 1575
  • 1576
  • 1577
  • 1578
  • 1579
  • 1580
  • 1581
  • 1582
  • 1583
  • 1584
  • 1585
  • 1586
  • 1587
  • 1588
  • 1589
  • 1590
  • 1591
  • 1592
  • 1593
  • 1594
  • 1595
  • 1596
  • 1597
  • 1598
  • 1599
  • 1600
  • 1601
  • 1602
  • 1603
  • 1604
  • 1605
  • 1606
  • 1607
  • 1608
  • 1609
  • 1610
  • 1611
  • 1612
  • 1613
  • 1614
  • 1615
  • 1616
  • 1617
  • 1618
  • 1619
  • 1620
  • 1621
  • 1622
  • 1623
  • 1624
  • 1625
  • 1626
  • 1627
  • 1628
  • 1629
  • 1630
  • 1631
  • 1632
  • 1633
  • 1634
  • 1635
  • 1636
  • 1637
  • 1638
  • 1639
  • 1640
  • 1641
  • 1642
  • 1643
  • 1644
  • 1645
  • 1646
  • 1647
  • 1648
  • 1649
  • 1650
  • 1651
  • 1652
  • 1653
  • 1654
  • 1655
  • 1656
  • 1657
  • 1658
  • 1659
  • 1660
  • 1661
  • 1662
  • 1663
  • 1664
  • 1665
  • 1666
  • 1667
  • 1668
  • 1669
  • 1670
  • 1671
  • 1672
  • 1673
  • 1674
  • 1675
  • 1676
  • 1677
  • 1678
  • 1679
  • 1680
  • 1681
  • 1682
  • 1683
  • 1684
  • 1685
  • 1686
  • 1687
  • 1688
  • 1689
  • 1690
  • 1691
  • 1692
  • 1693
  • 1694
  • 1695
  • 1696
  • 1697
  • 1698
  • 1699
  • 1700
  • 1701
  • 1702
  • 1703
  • 1704
  • 1705
  • 1706
  • 1707
  • 1708
  • 1709
  • 1710
  • 1711
  • 1712
  • 1713
  • 1714
  • 1715
  • 1716
  • 1717
  • 1718
  • 1719
  • 1720
  • 1721
  • 1722
  • 1723
  • 1724
  • 1725
  • 1726
  • 1727
  • 1728
  • 1729
  • 1730
  • 1731
  • 1732
  • 1733
  • 1734
  • 1735
  • 1736
  • 1737
  • 1738
  • 1739
  • 1740
  • 1741
  • 1742
  • 1743
  • 1744
  • 1745
  • 1746
  • 1747
  • 1748
  • 1749
  • 1750
  • 1751
  • 1752
  • 1753
  • 1754
  • 1755
  • 1756
  • 1757
  • 1758
  • 1759
  • 1760
  • 1761
  • 1762
  • 1763
  • 1764
  • 1765
  • 1766
  • 1767
  • 1768
  • 1769
  • 1770
  • 1771
  • 1772
  • 1773
  • 1774
  • 1775
  • 1776
  • 1777
  • 1778
  • 1779
  • 1780
  • 1781
  • 1782
  • 1783
  • 1784
  • 1785
  • 1786
  • 1787
  • 1788
  • 1789
  • 1790
  • 1791
  • 1792
  • 1793
  • 1794
  • 1795
  • 1796
  • 1797
  • 1798
  • 1799
  • 1800
  • 1801
  • 1802
  • 1803
  • 1804
  • 1805
  • 1806
  • 1807
  • 1808
  • 1809
  • 1810
  • 1811
  • 1812
  • 1813
  • 1814
  • 1815
  • 1816
  • 1817
  • 1818
  • 1819
  • 1820
  • 1821
  • 1822
  • 1823
  • 1824
  • 1825
  • 1826
  • 1827
  • 1828
  • 1829
  • 1830
  • 1831
  • 1832
  • 1833
  • 1834
  • 1835
  • 1836
  • 1837
  • 1838
  • 1839
  • 1840
  • 1841
  • 1842
  • 1843
  • 1844
  • 1845
  • 1846
  • 1847
  • 1848
  • 1849
  • 1850
  • 1851
  • 1852
  • 1853
  • 1854
  • 1855
  • 1856
  • 1857
  • 1858
  • 1859
  • 1860
  • 1861
  • 1862
  • 1863
  • 1864
  • 1865
  • 1866
  • 1867
  • 1868
  • 1869
  • 1870
  • 1871
  • 1872
  • 1873
  • 1874
  • 1875
  • 1876
  • 1877
  • 1878
  • 1879
  • 1880
  • 1881
  • 1882
  • 1883
  • 1884
  • 1885
  • 1886
  • 1887
  • 1888
  • 1889
  • 1890
  • 1891
  • 1892
  • 1893
  • 1894
  • 1895
  • 1896
  • 1897
  • 1898
  • 1899
  • 1900
  • 1901
  • 1902
  • 1903
  • 1904
  • 1905
  • 1906
  • 1907
  • 1908
  • 1909
  • 1910
  • 1911
  • 1912
  • 1913
  • 1914
  • 1915
  • 1916
  • 1917
  • 1918
  • 1919
  • 1920
  • 1921
  • 1922
  • 1923
  • 1924
  • 1925
  • 1926
  • 1927
  • 1928
  • 1929
  • 1930
  • 1931
  • 1932
  • 1933
  • 1934
  • 1935
  • 1936
  • 1937
  • 1938
  • 1939
  • 1940
  • 1941
  • 1942
  • 1943
  • 1944
  • 1945
  • 1946
  • 1947
  • 1948
  • 1949
  • 1950
  • 1951
  • 1952
  • 1953
  • 1954
  • 1955
  • 1956
  • 1957
  • 1958
  • 1959
  • 1960
  • 1961
  • 1962
  • 1963
  • 1964
  • 1965
  • 1966
  • 1967
  • 1968
  • 1969
  • 1970
  • 1971
  • 1972
  • 1973
  • 1974
  • 1975
  • 1976
  • 1977
  • 1978
  • 1979
  • 1980
  • 1981
  • 1982
  • 1983
  • 1984
  • 1985
  • 1986
  • 1987
  • 1988
  • 1989
  • 1990
  • 1991
  • 1992
  • 1993
  • 1994
  • 1995
  • 1996
  • 1997
  • 1998
  • 1999
  • 2000
  • 2001
  • 2002
  • 2003
  • 2004
  • 2005
  • 2006
  • 2007
  • 2008
  • 2009
  • 2010
  • 2011
  • 2012
  • 2013
  • 2014
  • 2015
  • 2016
  • 2017
  • 2018
  • 2019
  • 2020
  • 2021
  • 2022
  • 2023
  • 2024
  • 2025
  • 2026
  • 2027
  • 2028
  • 2029
  • 2030
  • 2031
  • 2032
  • 2033
  • 2034
  • 2035
  • 2036
  • 2037
  • 2038
  • 2039
  • 2040
  • 2041
  • 2042
  • 2043
  • 2044
  • 2045
  • 2046
  • 2047
  • 2048
  • 2049
  • 2050
  • 2051
  • 2052
  • 2053
  • 2054
  • 2055
  • 2056
  • 2057
  • 2058
  • 2059
  • 2060
  • 2061
  • 2062
  • 2063
  • 2064
  • 2065
  • 2066
  • 2067
  • 2068
  • 2069
  • 2070
  • 2071
  • 2072
  • 2073
  • 2074
  • 2075
  • 2076
  • 2077
  • 2078
  • 2079
  • 2080
  • 2081
  • 2082
  • 2083
  • 2084
  • 2085
  • 2086
  • 2087
  • 2088
  • 2089
  • 2090
  • 2091
  • 2092
  • 2093
  • 2094
  • 2095
  • 2096
  • 2097
  • 2098
  • 2099
  • 2100
  • 2101
  • 2102
  • 2103
  • 2104
  • 2105
  • 2106
  • 2107
  • 2108
  • 2109
  • 2110
  • 2111
  • 2112
  • 2113
  • 2114
  • 2115
  • 2116
  • 2117
  • 2118
  • 2119
  • 2120
  • 2121
  • 2122
  • 2123
  • 2124
  • 2125
  • 2126
  • 2127
  • 2128
  • 2129
  • 2130
  • 2131
  • 2132
  • 2133
  • 2134
  • 2135
  • 2136
  • 2137
  • 2138
  • 2139
  • 2140
  • 2141
  • 2142
  • 2143
  • 2144
  • 2145
  • 2146
  • 2147
  • 2148
  • 2149
  • 2150
  • 2151
  • 2152
  • 2153
  • 2154
  • 2155
  • 2156
  • 2157
  • 2158
  • 2159
  • 2160
  • 2161
  • 2162
  • 2163
  • 2164
  • 2165
  • 2166
  • 2167
  • 2168
  • 2169
  • 2170
  • 2171
  • 2172
  • 2173
  • 2174
  • 2175
  • 2176
  • 2177
  • 2178
  • 2179
  • 2180
  • 2181
  • 2182
  • 2183
  • 2184
  • 2185
  • 2186
  • 2187
  • 2188
  • 2189
  • 2190
  • 2191
  • 2192
  • 2193
  • 2194
  • 2195
  • 2196
  • 2197
  • 2198
  • 2199
  • 2200
  • 2201
  • 2202
  • 2203
  • 2204
  • 2205
  • 2206
  • 2207
  • 2208
  • 2209
  • 2210
  • 2211
  • 2212
  • 2213
  • 2214
  • 2215
  • 2216
  • 2217
  • 2218
  • 2219
  • 2220
  • 2221
  • 2222
  • 2223
  • 2224
  • 2225
  • 2226
  • 2227
  • 2228
  • 2229
  • 2230
  • 2231
  • 2232
  • 2233
  • 2234
  • 2235
  • 2236
  • 2237
  • 2238
  • 2239
  • 2240
  • 2241
  • 2242
  • 2243
  • 2244
  • 2245
  • 2246
  • 2247
  • 2248
  • 2249
  • 2250
  • 2251
  • 2252
  • 2253
  • 2254
  • 2255
  • 2256
  • 2257
  • 2258
  • 2259
  • 2260
  • 2261
  • 2262
  • 2263
  • 2264
  • 2265
  • 2266
  • 2267
  • 2268
  • 2269
  • 2270
  • 2271
  • 2272
  • 2273
  • 2274
  • 2275
  • 2276
  • 2277
  • 2278
  • 2279
  • 2280
  • 2281
  • 2282
  • 2283
  • 2284
  • 2285
  • 2286
  • 2287
  • 2288
  • 2289
  • 2290
  • 2291
  • 2292
  • 2293
  • 2294
  • 2295
  • 2296
  • 2297
  • 2298
  • 2299
  • 2300
  • 2301
  • 2302
  • 2303
  • 2304
  • 2305
  • 2306
  • 2307
  • 2308
  • 2309
  • 2310
  • 2311
  • 2312
  • 2313
  • 2314
  • 2315
  • 2316
  • 2317
  • 2318
  • 2319
  • 2320
  • 2321
  • 2322
  • 2323
  • 2324
  • 2325
  • 2326
  • 2327
  • 2328
  • 2329
  • 2330
  • 2331
  • 2332
  • 2333
  • 2334
  • 2335
  • 2336
  • 2337
  • 2338
  • 2339
  • 2340
  • 2341
  • 2342
  • 2343
  • 2344
  • 2345
  • 2346
  • 2347
  • 2348
  • 2349
  • 2350
  • 2351
  • 2352
  • 2353
  • 2354
  • 2355
  • 2356
  • 2357
  • 2358
  • 2359
  • 2360
  • 2361
  • 2362
  • 2363
  • 2364
  • 2365
  • 2366
  • 2367
  • 2368
  • 2369
  • 2370
  • 2371
  • 2372
  • 2373
  • 2374
  • 2375
  • 2376
  • 2377
  • 2378
  • 2379
  • 2380
  • 2381
  • 2382
  • 2383
  • 2384
  • 2385
  • 2386
  • 2387
  • 2388
  • 2389
  • 2390
  • 2391
  • 2392
  • 2393
  • 2394
  • 2395
  • 2396
  • 2397
  • 2398
  • 2399
  • 2400
  • 2401
  • 2402
  • 2403
  • 2404
  • 2405
  • 2406
  • 2407
  • 2408
  • 2409
  • 2410
  • 2411
  • 2412
  • 2413
  • 2414
  • 2415
  • 2416
  • 2417
  • 2418
  • 2419
  • 2420
  • 2421
  • 2422
  • 2423
  • 2424
  • 2425
  • 2426
  • 2427
  • 2428
  • 2429
  • 2430
  • 2431
  • 2432
  • 2433
  • 2434
  • 2435
  • 2436
  • 2437
  • 2438
  • 2439
  • 2440
  • 2441
  • 2442
  • 2443
  • 2444
  • 2445
  • 2446
  • 2447
  • 2448
  • 2449
  • 2450
  • 2451
  • 2452
  • 2453
  • 2454
  • 2455
  • 2456
  • 2457
  • 2458
  • 2459
  • 2460
  • 2461
  • 2462
  • 2463
  • 2464
  • 2465
  • 2466
  • 2467
  • 2468
  • 2469
  • 2470
  • 2471
  • 2472
  • 2473
  • 2474
  • 2475
  • 2476
  • 2477
  • 2478
  • 2479
  • 2480
  • 2481
  • 2482
  • 2483
  • 2484
  • 2485
  • 2486
  • 2487
  • 2488
  • 2489
  • 2490
  • 2491
  • 2492
  • 2493
  • 2494
  • 2495
  • 2496
  • 2497
  • 2498
  • 2499
  • 2500
  • 2501
  • 2502
  • 2503
  • 2504
  • 2505
  • 2506
  • 2507
  • 2508
  • 2509
  • 2510
  • 2511
  • 2512
  • 2513
  • 2514
  • 2515
  • 2516
  • 2517
  • 2518
  • 2519
  • 2520
  • 2521
  • 2522
  • 2523
  • 2524
  • 2525
  • 2526
  • 2527
  • 2528
  • 2529
  • 2530
  • 2531
  • 2532
  • 2533
  • 2534
  • 2535
  • 2536
  • 2537
  • 2538
  • 2539
  • 2540
  • 2541
  • 2542
  • 2543
  • 2544
  • 2545
  • 2546
  • 2547
  • 2548
  • 2549
  • 2550
  • 2551
  • 2552
  • 2553
  • 2554
  • 2555
  • 2556
  • 2557
  • 2558
  • 2559
  • 2560
  • 2561
  • 2562
  • 2563
  • 2564
  • 2565
  • 2566
  • 2567
  • 2568
  • 2569
  • 2570
  • 2571
  • 2572
  • 2573
  • 2574
  • 2575
  • 2576
  • 2577
  • 2578
  • 2579
  • 2580
  • 2581
  • 2582
  • 2583
  • 2584
  • 2585
  • 2586
  • 2587
  • 2588
  • 2589
  • 2590
  • 2591
  • 2592
  • 2593
  • 2594
  • 2595
  • 2596
  • 2597
  • 2598
  • 2599
  • 2600
  • 2601
  • 2602
  • 2603
  • 2604
  • 2605
  • 2606
  • 2607
  • 2608
  • 2609
  • 2610
  • 2611
  • 2612
  • 2613
  • 2614
  • 2615
  • 2616
  • 2617
  • 2618
  • 2619
  • 2620
  • 2621
  • 2622
  • 2623
  • 2624
  • 2625
  • 2626
  • 2627
  • 2628
  • 2629
  • 2630
  • 2631
  • 2632
  • 2633
  • 2634
  • 2635
  • 2636
  • 2637
  • 2638
  • 2639
  • 2640
  • 2641
  • 2642
  • 2643
  • 2644
  • 2645
  • 2646
  • 2647
  • 2648
  • 2649
  • 2650
  • 2651
  • 2652
  • 2653
  • 2654
  • 2655
  • 2656
  • 2657
  • 2658
  • 2659
  • 2660
  • 2661
  • 2662
  • 2663
  • 2664
  • 2665
  • 2666
  • 2667
  • 2668
  • 2669
  • 2670
  • 2671
  • 2672
  • 2673
  • 2674
  • 2675
  • 2676
  • 2677
  • 2678
  • 2679
  • 2680
  • 2681
  • 2682
  • 2683
  • 2684
  • 2685
  • 2686
  • 2687
  • 2688
  • 2689
  • 2690
  • 2691
  • 2692
  • 2693
  • 2694
  • 2695
  • 2696
  • 2697
  • 2698
  • 2699
  • 2700
  • 2701
  • 2702
  • 2703
  • 2704
  • 2705
  • 2706
  • 2707
  • 2708
  • 2709
  • 2710
  • 2711
  • 2712
  • 2713
  • 2714
  • 2715
  • 2716
  • 2717
  • 2718
  • 2719
  • 2720
  • 2721
  • 2722
  • 2723
  • 2724
  • 2725
  • 2726
  • 2727
  • 2728
  • 2729
  • 2730
  • 2731
  • 2732
  • 2733
  • 2734
  • 2735
  • 2736
  • 2737
  • 2738
  • 2739
  • 2740
  • 2741
  • 2742
  • 2743
  • 2744
  • 2745
  • 2746
  • 2747
  • 2748
  • 2749
  • 2750
  • 2751
  • 2752
  • 2753
  • 2754
  • 2755
  • 2756
  • 2757
  • 2758
  • 2759
  • 2760
  • 2761
  • 2762
  • 2763
  • 2764
  • 2765
  • 2766
  • 2767
  • 2768
  • 2769
  • 2770
  • 2771
  • 2772
  • 2773
  • 2774
  • 2775
  • 2776
  • 2777
  • 2778
  • 2779
  • 2780
  • 2781
  • 2782
  • 2783
  • 2784
  • 2785
  • 2786
  • 2787
  • 2788
  • 2789
  • 2790
  • 2791
  • 2792
  • 2793
  • 2794
  • 2795
  • 2796
  • 2797
  • 2798
  • 2799
  • 2800
  • 2801
  • 2802
  • 2803
  • 2804
  • 2805
  • 2806
  • 2807
  • 2808
  • 2809
  • 2810
  • 2811
  • 2812
  • 2813
  • 2814
  • 2815
  • 2816
  • 2817
  • 2818
  • 2819
  • 2820
  • 2821
  • 2822
  • 2823
  • 2824
  • 2825
  • 2826
  • 2827
  • 2828
  • 2829
  • 2830
  • 2831
  • 2832
  • 2833
  • 2834
  • 2835
  • 2836
  • 2837
  • 2838
  • 2839
  • 2840
  • 2841
  • 2842
  • 2843
  • 2844
  • 2845
  • 2846
  • 2847
  • 2848
  • 2849
  • 2850
  • 2851
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/767694
推荐阅读
相关标签
  

闽ICP备14008679号