stm32

STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器

ARM内核

stm32F103C8T6采用Cortex-M3内核,发布于2004年10月。

片上资源/外设

英文缩写 名称 英文缩写 名称
NVIC 嵌套向量中断控制器 CAN CAN通信
SysTick 系统滴答定时器 USB USB通信
RCC 复位和时钟控制 RTC 实时时钟
GPIO 通用IO口 CRC CRC校验
AFIO 复用IO口 PWR 电源控制
EXTI 外部中断 BKP 备份寄存器
TIM 定时器 IWDG 独立看门狗
ADC 模数转换器 WWDG 窗口看门狗
DMA 直接内存访问 DAC 数模转换器
USART 同步/异步串口通信 SDIO SD卡接口
I2C I2C通信 FSMC 可变静态存储控制器
SPI SPI通信 USB OTG USB主机接口

命名规则

系统结构

引脚定义

红色:电源相关引脚 蓝色:最小系统相关引脚 绿色:IO口、功能口

有FT可以接上5V电压,没有FT只能容忍3.3V电压。

主功能:上电默认功能

stm32采用分区供电,有多个供电引脚

存储器映像

启动配置

  • 从主闪存存储器启动:主闪存存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(0x0800 0000)访问它,即闪存存储器的内容可以在两个地址区域访问,0x0000 0000或0x0800 0000。
  • 从系统存储器启动:系统存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址访问它。
  • 从内置SRAM启动:只能在0x2000 0000开始的地址区访问SRAM。
  • 主闪存存储器模式,正常执行闪存里面的程序。
  • 系统存储器模式,串口下载,系统存储器存储bootlooder程序。内嵌的自举程序存放在系统存储区,由ST在生产线上写入,用于通过可用的串行接口对闪存存储器进行重新编程。
  • 内置SRAM模式,程序调试。

  • 在系统复位后,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。即引脚20中的BOOT1功能转变为PB2功能

最小系统电路

STM32核心板

C语言数据类型

关键字 位数 表示范围 stdint关键字 ST关键字
char 8 -128 ~ 127 int8_t s8
unsigned char 8 0 ~ 255 uint8_t u8
short 16 -32768 ~ 32767 int16_t s16
unsigned short 16 0 ~ 65535 uint16_t u16
int 32 -2147483648 ~ 2147483647 int32_t s32
unsigned int 32 0 ~ 4294967295 uint32_t u32
long 32 -2147483648 ~ 2147483647
unsigned long 32 0 ~ 4294967295
long long 64 -(2^64)/2 ~ (2^64)/2-1 int64_t
unsigned long long 64 0 ~ (2^64)-1 uint64_t
float 32 -3.4e38 ~ 3.4e38
double 64 -1.7e308 ~ 1.7e308

typedef和define的区别

(1)原理不同

define是C语言中定义的语法,是预处理指令,在预处理时进行简单而机械的字符串替换,不作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。

typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。用typedef定义数组、指针、结构等类型会带来很大的方便,不仅使程序书写简单,也使意义明确,增强可读性。

(2)功能不同

typedef用来定义类型的别名,起到类型易于记忆的功能。另一个功能是定义机器无关的类型。如定义一个REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL, 在不支持long double的机器上,看起来是这样的,typedef double REAL,在不支持double的机器上,是这样的,typedef float REAL

define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

(3)作用域不同

define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而typedef有自己的作用域。

typedef和define有什么区别_define和typedef的区别-CSDN博客

结构体struct:数据打包,不同类型变量的集合,因为结构体变量类型较长,所以通常用typedef更改变量类型名。

枚举enum:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合

STM32新建工程

工程架构

步骤

  • 建立工程文件夹,Keil中新建工程,选择型号
  • 工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹,保持工程独立性
  • 工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
  • 工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
  • 工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
  • 工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run

这里比较复杂直接看视频就行,下面是一个demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "stm32f10x.h"                  // Device header

int main(void)
{
// RCC->APB2ENR = 0x00000010; 使用寄存器
// GPIOC->CRH = 0x00300000;
// GPIOC->ODR = 0x00002000;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //使用标准库
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
// GPIO_SetBits(GPIOC,GPIO_Pin_13); //设置高电平,灭灯
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //设置低电平,电灯
while(1)
{

}

}

GPIO

基本结构

GPIO位结构

  • 推挽模式(强推输出模式):P-MOS,N-MOS均有效,STM32对IO口有绝对的控制权。
  • 开漏模式:只有N-MOS工作,只有低电平有驱动能力,可以作为通信协议的驱动方式,输出5V电平信号
  • 关闭模式:输出关闭,端口的电平由外部信号控制

GPIO模式

模式名称 性质 特征
浮空输入 数字输入 可读取引脚电平,若引脚悬空,则电平不确定
上拉输入 数字输入 可读取引脚电平,内部连接上拉电阻,悬空时默认高电平
下拉输入 数字输入 可读取引脚电平,内部连接下拉电阻,悬空时默认低电平
模拟输入 模拟输入 GPIO无效,引脚直接接入内部ADC
开漏输出 数字输出 可输出引脚电平,高电平为高阻态,低电平接VSS
推挽输出 数字输出 可输出引脚电平,高电平接VDD,低电平接VSS
复用开漏输出 数字输出 由片上外设控制,高电平为高阻态,低电平接VSS
复用推挽输出 数字输出 由片上外设控制,高电平接VDD,低电平接VSS
  • LED:发光二极管,正向通电点亮,反向通电不亮

  • 有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定

  • 无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音

LED

硬件电路

LED一般采用方式1

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
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //设置结构体对应的引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //频率
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化


// GPIO_ResetBits(GPIOA, GPIO_Pin_0); 低电平
// GPIO_SetBits(GPIOA, GPIO_Pin_0); 高电平
// GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); 高电平

// GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); 低电平
while(1)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
Delay_ms(25);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
Delay_ms(25);
}

}

LED流水灯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{
int i = 0;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
for(i=0;i<8;i++)
{
GPIO_Write(GPIOA,~(0x0001<<i));
Delay_ms(500);
}
}
}

恭喜您达成成就~——点灯大师!

蜂鸣器

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
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{
int i = 0;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure1;
GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure1);
while(1)
{
for(i=0;i<8;i++)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
GPIO_Write(GPIOA,~(0x0001<<i));
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(100);
}
}
}

按键控制LED

硬件电路

  • 传感器模块:传感器元件(光敏电阻/热敏电阻/红外接收管等)的电阻随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压出,再通过电压比较器进行二值化即可得到数字电压输出

按键一般用上两种方式

左边两种接法必须要求引脚是上拉或下拉输出

右边两种接法允许引脚是浮空输入的模式

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
/*						key.c					*/
#include "stm32f10x.h"
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //对输入无影响
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);

}

uint8_t Key_GetNum(void)
{
uint8_t KeyNum=0;
if(!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1))//读输入数据寄存器值
{
Delay_ms(50);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1));//消抖
Delay_ms(50);
KeyNum=1;
}
if(!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11))
{
Delay_ms(50);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11));
Delay_ms(50);
KeyNum=11;
}

return KeyNum;

}
/* key.c */


/* led.c */
#include "stm32f10x.h"

void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);

}


void LED_ON(uint16_t Pin) //开启
{
GPIO_ResetBits(GPIOA, Pin );
}


void LED_OFF(uint16_t Pin) //关闭
{
GPIO_SetBits(GPIOA, Pin );
}
void LED2_TURN(void) //翻转
{
if(!GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2)) //读输出数据寄存器值
GPIO_SetBits(GPIOA, GPIO_Pin_2);
else
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
/* led.c */

/* main.c */
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t Key;
int main(void)
{
LED_Init();
Key_Init();
while(1)
{
Key = Key_GetNum();
if(Key == 11)
LED2_TURN();

if(Key == 1)
LED_ON(GPIO_Pin_1);
else
LED_OFF(GPIO_Pin_1);
Delay_ms(100);

}
}
/* main.c */

蜂鸣器

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
/*						main.c					*/
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"

int main(void)
{
Buzzer_Init();
LightSensor_Init();
while(1) //被黑暗遮挡发出尖锐爆鸣声音
{
if(LightSensor_Get() == 1)
Buzzer_ON();
else
Buzzer_OFF();
}
}
/* main.c */


/* LightSensor.c */
#include "stm32f10x.h"

void LightSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
GPIO_Init(GPIOB, &GPIO_InitStructure);

}



uint8_t LightSensor_Get(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);

}



/* LightSensor.c */

/* Buzzer.c */
#include "stm32f10x.h"

void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_Init(GPIOB, &GPIO_InitStructure);

}


void Buzzer_ON(void) //开启
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}


void Buzzer_OFF(void) //关闭
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}



void Buzzer_TURN(void) //翻转
{
if(!GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12)) //读输出数据寄存器值
GPIO_SetBits(GPIOB, GPIO_Pin_12);
else
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

/* Buzzer.c */


OLED 调试模块