江科大自化协(stm32) STM32F103RCT6产品参数
产品型号
内核
主频(MHz)
Flash (Kbytes)
STM32F103RCT6
Cortex-M3
72
256
RAM(Kbytes)
E2PROM(Bytes)
封装
IO
48
0
LQFP64
51
工作电压
16位定时器
32位定时器
电机控制定时器 (16-bit)
2-3.6
8
0
2
低功耗定时器
高分辨率定时器
12位ADC转换单元
12位ADC通道
0
0
3
16
14位ADC转换单元
14位ADC通道
16位ADC转换单元
16位ADC通道
0
0
0
0
12位DAC通道
比较器
放大器
SPI
2
0
0
3
I2S
M-SPI
I2C
U(S)ART
2
0
2
5
低功耗UART
CAN
SDIO
F(S)MC
0
1
1
0
USB Device
USB FS HOST/OTG
USB HS OTG
Ethernet
1
0
0
0
MDIO
Segment LCD
JPEG Codec
GPU
0
0
N/A
N/A
3D GPU
TFT LCD
MIPI_DSI
SAI
N/A
0
0
0
SPDIFRX
DFSDM
DCMI
SWPMI
0
0
0
0
Math Accelerator
RF
Trust’Zone
TRNG
N/A
N/A
N/A
N/A
OTFDEC
PKA
AES/DES
SHA/HMAC
N/A
N/A
N/A
N/A
T° Max(℃)
URL
content
85
https://www.st.com/en/product/STM32F103RC
STM32F103RCT6引脚定义
输入输出形式:GPIO的八种工作模式
模式名称
性质
特征
IN_FLOATING浮空输入
数字输入
可读取引脚电平,若引脚悬空,则电平不确定
IPU上拉输入
数字输入
可读取引脚电平,内部连接上拉电阻,悬空时默认高电平
IPD下拉输入
数字输入
可读取引脚电平,内部连接下拉电阻,悬空时默认低电平
AIN模拟输入
模拟输入
GPIO无效,引脚直接接入内部ADC
Out_OD开漏输出
数字输出
可输出引脚电平,高电平为高阻态,低电平接VSS
Out_PP推挽输出
数字输出
可输出引脚电平,高电平接VDD,低电平接VSS
AF_OD复用开漏输出
数字输出
由片上外设控制,高电平为高阻态,低电平接VSS
AF_PP复用推挽输出
数字输出
由片上外设控制,高电平接VDD,低电平接VSS
推挽输出Out_PP与开漏输出Out_OD: 推挽输出高低电平均有驱动能力.
开漏输出高电平相当于高阻态,没有驱动能力,低电平有驱动能力.
(一般输出用推挽就行,特殊采用开漏)
VCC,VDD,VSS: 电压的作用对象不同:VCC的供电电压作用于电路。VDD的工作电压作用于芯片。VSS的电压作用于器件内部。
来源不同:VDD来源于漏极电源电压,用于 MOS 晶体管电路, 一般指正电源。 Vss来源于极电源电压,在 CMOS 电路中指负电源, 在单电源时指零伏或接地。
GPIO以及AFIO各个函数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void GPIO_DeInit (GPIO_TypeDef* GPIOx) ;void GPIO_AFIODeInit (void ) ;void GPIO_Init (GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) ;void GPIO_StructInit (GPIO_InitTypeDef* GPIO_InitStruct) ;uint8_t GPIO_ReadInputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;uint16_t GPIO_ReadInputData (GPIO_TypeDef* GPIOx) ;uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;uint16_t GPIO_ReadOutputData (GPIO_TypeDef* GPIOx) ;void GPIO_SetBits (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;void GPIO_ResetBits (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;void GPIO_WriteBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal) ;void GPIO_Write (GPIO_TypeDef* GPIOx, uint16_t PortVal) ;void GPIO_PinLockConfig (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;void GPIO_EventOutputConfig (uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) ;void GPIO_EventOutputCmd (FunctionalState NewState) ;void GPIO_PinRemapConfig (uint32_t GPIO_Remap, FunctionalState NewState) ;void GPIO_EXTILineConfig (uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) ;void GPIO_ETH_MediaInterfaceConfig (uint32_t GPIO_ETH_MediaInterface) ;
stm新建工程启动文件后缀意义 (stm32F103RCT6的容量是256kb)
缩写
释义
Flash****容量
型号
LD_VL
小容量产品超值系列
16~32K
STM32F100
MD_VL
中容量产品超值系列
64~128K
STM32F100
HD_VL
大容量产品超值系列
256~512K
STM32F100
LD
小容量产品
16~32K
STM32F101/102/103
MD
中容量产品
64~128K
STM32F101/102/103
HD
大容量产品
256~512K
STM32F101/102/103
XL
加大容量产品
大于512K
STM32F101/102/103
CL
互联型产品
-
STM32F105/107
LED,蜂鸣器,按键电路图示
STM32低电平点亮LED灯。低电平强驱动 STM32高电平点亮LED灯。高电平弱驱动
PNP三极管驱动电路(左边是基极,带箭头的是发射极,剩下的是集电极)(左边基极给低电平,三极管就会导通,通过3.3v和GND,就可以给蜂鸣器提供驱动电流,反之基极给高电平,截止,没有电流)(PNP三极管最好接上边,这是因为三极管的通断是需要在发射极和基极直接产生一定的开启电压 )
NPN三极管驱动电路(左边是基极,带箭头的是发射极,剩下的是集电极)(基极给高电平导通,低电平断开)(NPN三极管最好接下边, 这是因为三极管的通断是需要在发射极和基极直接产生一定的开启电压 )
按键图示(PA0内部必须是上拉输入模式,则此时按键松开引脚悬空输入的是高电平,按下按键连接GND以致PA是低电平)
按键图示(已经存在上拉电阻,PA0可以是上拉输入(内外上拉电阻共同作用)也可以是浮空输入)按下为低电平,松开为高电平
(拓展,不常用)按键图示(下拉输入,不可浮空输入)按下是高电平,松开是低电平
(拓展,不常用)按键图示(下拉输入或者浮空输入)按下是高电平,松开是低电平
传感器模块电路图示:DO数字输出,AO模拟输出
OLED文件提供: 引脚配置与引脚初始化需要按照实际板子样式配置,其余不需要更改。
OLED显示屏总共4行16列(1行一列开头,无0行0列)
函数
作用
OLED_Init();
初始化
OLED_Clear();
清屏
OLED_ShowChar(1, 1, ‘A’);
显示一个字符
OLED_ShowString(1, 3, “HelloWorld!”);
显示字符串
OLED_ShowNum(2, 1, 12345, 5);
显示十进制数字
OLED_ShowSignedNum(2, 7, -66, 2);
显示有符号十进制数字
OLED_ShowHexNum(3, 1, 0xAA55, 4);
显示十六进制数字
OLED_ShowBinNum(4, 1, 0xAA55, 16);
显示二进制数字
(上表第四个数字为长度)(C语言不能直接写二进制的数)
C语言知识拓展 C语言数字类型 (“int”数据在51单片机中占16位,在STM32中占32位)(32想表示16位的数据得用“short”)(stdint.h头文件与ST库函数对这些变量的重命名)
关键字
位数
表示范围
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
extern (外部变量,跨越工程下的不同文件)
typedef,define,结构体,枚举 结构体eg:
1 2 3 StructName.z = 1.23 ; pStructName->z = 1.23 ;
枚举:关键字:enum 用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合 定义枚举变量:enum{FALSE = 0, TRUE = 1} EnumName; 因为枚举变量类型较长,所以通常用typedef更改变量类型名 引用枚举成员: EnumName = FALSE; EnumName = TRUE;
enum{MONDAY=0,TUSDAY,WEDNESDAY}week;//后面的值可不赋,系统自动按顺序赋值0,1,2,3,4~~~
STM32中断 68个可屏蔽中断通道,包含EXTI(外部中断)、TIM(定时中断)、ADC(模数转换器)、USART(串口)、SPI(通信)、I2C(通信)、RTC(实时时钟)等多个外设
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级(NVIC是用来管理中断,分配16个优先级的)
中断函数模块示例 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 #include "stm32f10x.h" uint16_t CountSensor_Count;void CountSensor_Init (void ) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14); EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line=EXTI_Line14 ; EXTI_InitStructure.EXTI_LineCmd=ENABLE; EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling ; EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority=1 ; NVIC_Init(&NVIC_InitStructure); } void EXTI15_10_IRQHandler (void ) { if (EXTI_GetITStatus(EXTI_Line14)==SET) { CountSensor_Count++; EXTI_ClearITPendingBit(EXTI_Line14); } } uint16_t CountSensor_Count_Get (void ) { return CountSensor_Count; }
NVIC基本结构 (NVIC是一个内核外设,是CPU的小助手)
NVIC优先级分组 NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
分组方式
抢占优先级
响应优先级
分组0
0位,取值为0
4位,取值为0~15
分组1
1位,取值为0~1
3位,取值为0~7
分组2
2位,取值为0~3
2位,取值为0~3
分组3
3位,取值为0~7
1位,取值为0~1
分组4
4位,取值为0~15
0位,取值为0
NVIC函数 (在中断配置之前,先指定一下中断的分组,再初始化NVIC)(分组的方式整个芯片只能用一种,分组的代码整个工程只需要执行一次)(如果把它放在模块里进行分组,要保证每个模块分组都选的是同一个,或者把这个代码放在主函数的最开始,这样模块里就不用再进行分组了)
1 2 3 4 5 void NVIC_PriorityGroupConfig (uint32_t NVIC_PriorityGroup) ;void NVIC_Init (NVIC_InitTypeDef* NVIC_InitStruct) ;void NVIC_SetVectorTable (uint32_t NVIC_VectTab, uint32_t Offset) ;void NVIC_SystemLPConfig (uint8_t LowPowerMode, FunctionalState NewState) ;void SysTick_CLKSourceConfig (uint32_t SysTick_CLKSource) ;
中断详情 灰色部分:内核的中断(比较高深,一般用不到)
白色部分:STM32外设的中断
WWDG(窗口定时器中断:窗口看门狗)
EXTI外部中断 EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序 支持的触发方式:上升沿/下降沿/双边沿/软件触发(引脚啥事没有,程序里一句代码就能执行中断) 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(eg.PA0与PB0不能同时用)(后面四个其实是来外部中断蹭网的,因为外部中断能从低功耗模式的停止模式下唤醒STM32) 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒(总共20个中断线路) 触发响应方式:中断响应(申请中断,让CPU执行中断)/事件响应(当外部中断检测到引脚电平变化时,选择触发事件,外部中断的信号就不会通向CPU了而是通向·触发其他外设(属于外设之间的联合操作))
EXTI框图
EXTI函数 1 2 3 4 5 6 7 8 9 10 11 12 void EXTI_DeInit (void ) ;void EXTI_Init (EXTI_InitTypeDef* EXTI_InitStruct) ;void EXTI_StructInit (EXTI_InitTypeDef* EXTI_InitStruct) ;void EXTI_GenerateSWInterrupt (uint32_t EXTI_Line) ;FlagStatus EXTI_GetFlagStatus (uint32_t EXTI_Line) ; void EXTI_ClearFlag (uint32_t EXTI_Line) ;ITStatus EXTI_GetITStatus (uint32_t EXTI_Line) ; void EXTI_ClearITPendingBit (uint32_t EXTI_Line) ;
AFIO复用IO口 AFIO主要用于引脚复用功能的选择和重定义
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
旋转编码器 用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向 类型:机械触点式/霍尔传感器式/光栅式
旋转编码器视图及框架
1 2 void GPIO_AFIODeInit (void ) ;
STM32->TIM定时器 定时器通道引脚
类型
编号
总线
功能
高级定时器
TIM1、TIM8
APB2
拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
通用定时器
TIM2、TIM3、TIM4、TIM5
APB1
拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器
TIM6、TIM7
APB1
拥有定时中断、主模式触发DAC的功能
TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
定时器图示 高级定时器图示
(增加了重复计数寄存器,原先结构每个计数周期结束都会发生更新,这里可以实现每隔几个技术周期发生更新(相当于对输出的更新信号又做了一次分频))
DTG(Dead Time Generate死区生成电路)(右边的输出引脚由一个变成了两个互补的输出,输出一对互补的PWM波(为了驱动三项无刷电机(三项无刷电机的驱动电路需要三个桥臂,每个桥臂需要两个大功率开关管控制,总共需要六个大功率开关)为了防止互补输出的PWM驱动桥臂时,在开关切换的瞬间,由于器件的不理想,造成短暂的直通现象,所以前面加上死区生成电路,在开关切换的瞬间,产生一定时长的死区,让桥臂的上下管全部关断,防止直通现象))
(增加了刹车输入功能(为了给电机驱动提供安全保障)如果外部引脚BKIN(break in)产生了刹车信号或者内部时钟失效产生故障,那么控制电路就会自动切断电机的输出,防止意外发生)
通用定时器图示(计数器向上计数,向下计数,中央对齐计数模式)
外部时钟模式2:想在ETR外部引脚提供时钟或者对ETR时钟进行计数,把定时器当作定时器用(外部时钟首选)
外部时钟模式1:将TRGI触发输入当作外部时钟输入(通过这一路的外部时钟有1.ETR引脚信号(这一路输入会占用触发输入的通道)2.ITR信号(这一部分时钟信号来自其他定时器)3.CH1引脚的边沿(上升沿下降沿都可)4.CH1引脚和CH2引脚)(通过外部时钟模式1的ITR,TRGO连接方式可以实现定时器的级联)
(外部ETR引脚和TRGI可以提供时钟)(TRGI用作触发输入,可以触发定时器的从模式))(在此处将TRGI触发输入当作外部时钟输入)(主模式的输出TRGO可以通向其他定时器的ITR引脚)(ITR0~ITR3分别来自其他四个定时器的输出,如下表TIM2的ITR0是接在TIM1的TRGO上的,TIM2的ITR1是接在TIM8的TRGO上,以此类推)
从定时器
ITR0 (TS = 000)
ITR1 (TS = 001)
ITR2 (TS = 010)
ITR3 (TS = 011)
TIM2
TIM1
TIM8
TIM3
TIM4
TIM3
TIM1
TIM2
TIM5
TIM4
TIM4
TIM1
TIM2
TIM3
TIM8
TIM5
TIM2
TIM3
TIM4
TIM8
基本定时器图示(计数器向上计数)
(产生更新中断或者更新事件)(更新事件不会触发中断,但可以触发内部其他电路的工作)
定时器定时中断流程: (内部时钟)基准时钟–>预分频器–>计数器–>计数器计数自增,同时不断与自动重装寄存器进行比较–>它俩值相等时,计时时间到,产生一个更新中断或者更新事–>CPU响应更新中断,完成定时中断的任务
主从触发模式 (STM32定时器一大特色)它能让内部的硬件在不受程序的控制下实现自动运行
eg:主模式触发DAC:当用DAC输出一段波形时,需要每隔一段时间来触发DAC,让它输出下一个电压点–>定时器设计了一个主模式,使用这个主模式可以把这个定时器的更新事件映射到这个触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚上。
定时中断基本结构
(中断输出控制就是中断输出的允许位)
时序图 预分频器时序图 CK_PSC:预分频器的输入时钟
CNT_EN:计数器使能(高电平计数器正常运行,低电平计数器停止)
CK_CNT:计数器时钟(预V分频器的时钟输出,计数器的时钟输入)(看图,前半段,计数器未使能,计数器/定时器时钟不运行,使能后,前半段,预分频器系数为1,计数器时钟等于预分频器时钟,后半段,预分频器系数为2,计数器时钟等于预分频器时钟的一半)(预分频器系数=预分频值+1)(在计数器时钟的驱使下,计数器寄存器也跟着时钟的上升沿而不断自增)
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
计数器时序图 CK_INT:内部时钟72MHz
CNT_EN:时钟使能,高电平启动
CK_CNT:计数器时钟/定时器时钟:(eg:频率为CK_CNT/分频系数=36MHz)
更新中断标志位UIF:只要置1了,就回去申请中断,中断响应后,需要手动 清零
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
溢出时间=1/溢出频率
(ARR:自动重装载值)
计数器无预装时序图 (无缓冲寄存器/影子寄存器)
计数器有预装时序图 (有缓冲寄存器/影子寄存器)
(让值的变化与更新事件同步发生,防止在运行途中更改造成错误)
RCC时钟树 STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统(时钟是所有外设运行的基础,是最先需要配置的东西(主函数运行前执行的SystemInit函数就是用来配置RCC时钟树的))
图示左侧都是时钟的产生电路,右侧都是时钟的分配电路(SYSCLK->系统时钟72MHz)(时钟产生电路:四个震荡源:内部的8MHz高速RC振荡器,外部的4-16MHz高速石英晶体振荡器—>晶振(一般8MHzx),外部的32.768KHz低速晶振->给RTC提供时钟,内部40KHz低速RC振荡器->给看门狗提供时钟)(AHB,APB1,APB2的时钟来自这两个高速晶振)
TIM输出比较 输出比较:OC
输出比较八种模式
TIM函数 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 void TIM_DeInit (TIM_TypeDef* TIMx) ;void TIM_TimeBaseInit (TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct) ;void TIM_TimeBaseStructInit (TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct) ;void TIM_Cmd (TIM_TypeDef* TIMx, FunctionalState NewState) ;void TIM_ITConfig (TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState) ;void TIM_InternalClockConfig (TIM_TypeDef* TIMx) ;void TIM_ITRxExternalClockConfig (TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource) ;void TIM_TIxExternalClockConfig (TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter) ;void TIM_ETRClockMode1Config (TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter) ;void TIM_ETRClockMode2Config (TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter) ;void TIM_ETRConfig (TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter) ;void TIM_PrescalerConfig (TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode) ;void TIM_CounterModeConfig (TIM_TypeDef* TIMx, uint16_t TIM_CounterMode) ;void TIM_ARRPreloadConfig (TIM_TypeDef* TIMx, FunctionalState NewState) ;void TIM_SetCounter (TIM_TypeDef* TIMx, uint16_t Counter) ;void TIM_SetAutoreload (TIM_TypeDef* TIMx, uint16_t Autoreload) ;uint16_t TIM_GetCounter (TIM_TypeDef* TIMx) ;uint16_t TIM_GetPrescaler (TIM_TypeDef* TIMx) ;FlagStatus TIM_GetFlagStatus (TIM_TypeDef* TIMx, uint16_t TIM_FLAG) ; void TIM_ClearFlag (TIM_TypeDef* TIMx, uint16_t TIM_FLAG) ;ITStatus TIM_GetITStatus (TIM_TypeDef* TIMx, uint16_t TIM_IT) ; void TIM_ClearITPendingBit (TIM_TypeDef* TIMx, uint16_t TIM_IT) ;void TIM_OC1Init (TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) ;void TIM_OC2Init (TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) ;void TIM_OC3Init (TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) ;void TIM_OC4Init (TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct) ;void TIM_OCStructInit (TIM_OCInitTypeDef* TIM_OCInitStruct) ;void TIM_ForcedOC1Config (TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) ;void TIM_ForcedOC2Config (TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) ;void TIM_ForcedOC3Config (TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) ;void TIM_ForcedOC4Config (TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction) ;void TIM_OC1PreloadConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) ;void TIM_OC2PreloadConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) ;void TIM_OC3PreloadConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) ;void TIM_OC4PreloadConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) ;void TIM_OC1FastConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCFast) ;void TIM_OC2FastConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCFast) ;void TIM_OC3FastConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCFast) ;void TIM_OC4FastConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCFast) ;void TIM_ClearOC1Ref (TIM_TypeDef* TIMx, uint16_t TIM_OCClear) ;void TIM_ClearOC2Ref (TIM_TypeDef* TIMx, uint16_t TIM_OCClear) ;void TIM_ClearOC3Ref (TIM_TypeDef* TIMx, uint16_t TIM_OCClear) ;void TIM_ClearOC4Ref (TIM_TypeDef* TIMx, uint16_t TIM_OCClear) ;void TIM_OC1PolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) ;void TIM_OC1NPolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity) ;void TIM_OC2PolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) ;void TIM_OC2NPolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity) ;void TIM_OC3PolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) ;void TIM_OC3NPolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity) ;void TIM_OC4PolarityConfig (TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity) ;void TIM_CCxCmd (TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx) ;void TIM_CCxNCmd (TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN) ;void TIM_SelectOCxM (TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode) ;void TIM_SetCompare1 (TIM_TypeDef* TIMx, uint16_t Compare1) ;void TIM_SetCompare2 (TIM_TypeDef* TIMx, uint16_t Compare2) ;void TIM_SetCompare3 (TIM_TypeDef* TIMx, uint16_t Compare3) ;void TIM_SetCompare4 (TIM_TypeDef* TIMx, uint16_t Compare4) ;void TIM_CtrlPWMOutputs (TIM_TypeDef* TIMx, FunctionalState NewState) ;
PWM基本结构
把模块都打通,就可以输出PWM了
1.RCC开启时钟,把要用的TIM外设和GPIO外设的时钟打开
2.配置时基单元,包括前面的时钟源选择
3.配置输出比较单元,包括CCR的值,输出比较模式,极性选择,输出使能等参数(在库函数里用结构体来配置)
4。配置GPIO,把PWM对应的GPIO口初始化为复用推挽输出
5。运行控制,启动计数器,输出PWM
串口通信
串口参数 波特率:串口通信的速率 起始位:标志一个数据帧的开始,固定为低电平 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行 校验位:用于数据验证,根据数据位计算得来 停止位:用于数据帧间隔,固定为高电平
串口中断 串口内部有两个寄存器:发送数据寄存器(TDR),发送移位寄存器
STM调试 keil 5调试
RTC实现 RTC代码实现所需函数 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 void BKP_DeInit (void ) ;void RCC_LSEConfig (uint8_t RCC_LSE) ;void BKP_WriteBackupRegister (uint16_t BKP_DR, uint16_t Data) ;uint16_t BKP_ReadBackupRegister (uint16_t BKP_DR) ;FlagStatus RCC_GetFlagStatus (uint8_t RCC_FLAG) ; void RCC_RTCCLKConfig (uint32_t RCC_RTCCLKSource) ;void RCC_RTCCLKCmd (FunctionalState NewState) ;void RTC_WaitForSynchro (void ) ;void RTC_WaitForLastTask (void ) ;void RTC_ITConfig (uint16_t RTC_IT, FunctionalState NewState) ;void RTC_EnterConfigMode (void ) ;void RTC_ExitConfigMode (void ) ;void RTC_SetPrescaler (uint32_t PrescalerValue) ;void RTC_ClearFlag (uint16_t RTC_FLAG) ;uint32_t RTC_GetCounter (void ) ;void RTC_SetCounter (uint32_t CounterValue) ;void RTC_SetAlarm (uint32_t AlarmValue) ;ITStatus RTC_GetITStatus (uint16_t RTC_IT) ; void RTC_ClearITPendingBit (uint16_t RTC_IT) ;
RTC配置流程 使能PWR和BKP时钟
使能后备寄存器访问
配置RTC时钟源,使能RTC时钟
如果使用LSE,要打开LSE
配置RTC预分频器系数
设置时间
开启相关中断(如果需要)
编写中断服务函数
部分操作要等待写操作和完成和同步
I2C SCL和SDA应该外挂上拉电阻
软件I2C 不用看I2C的库函数,只需要用GPIO的读取函数
软件I2C初始化
把SCL和SDA都初始化为开漏输出模式
把SCL和SDA都置高电平
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void MyI2C_Init (void ) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_10|GPIO_Pin_11); }
完成I2C的六个时序基本单元
(所有的单元都会保证以SCL低电平结束)
0,封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void MyI2C_W_SCL (uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue); Delay_us(10 ); } void MyI2C_W_SDA (uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue); Delay_us(10 ); } uint8_t MyI2C_R_SDA (void ) { uint8_t BitValue; BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11); Delay_us(10 ); return BitValue; }
1.起始条件
1 2 3 4 5 6 7 8 9 void MyI2C_Start (void ) { MyI2C_W_SDA(1 ); MyI2C_W_SCL(1 ); MyI2C_W_SDA(0 ); MyI2C_W_SCL(0 ); }
2.终止条件
1 2 3 4 5 6 7 void MyI2C_Stop (void ) { MyI2C_W_SDA(0 ); MyI2C_W_SCL(1 ); MyI2C_W_SDA(1 ); }
3.发送一个字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void MyI2C_SendByte (uint8_t Byte) { uint8_t i; for (i=0 ;i<8 ;i++) { MyI2C_W_SDA(Byte&(0x80 >>i)); MyI2C_W_SCL(1 ); MyI2C_W_SCL(0 ); } }
4.接收一个字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 uint8_t MyI2C_ReceiveByte (void ) { uint8_t i, Byte =0x00 ; MyI2C_W_SDA(1 ); for (i=0 ;i<8 ;i++) { MyI2C_W_SCL(1 ); if (MyI2C_R_SDA()==1 ) {Byte |=( MyI2C_W_SCL(1 ); if (MyI2C_R_SDA()==1 ) {Byte |=0x80 ;}>>i);} MyI2C_W_SCL(0 ); } return Byte; }
5.发送应答,接收应答 (发送一个字节就是发8位,发送应答就是发1位)接收应答(接收一个字节就是收八位,接收应答就是收一位)
1 2 3 4 5 6 7 8 9 10 void MyI2C_SendAck (uint8_t AckBit) { MyI2C_W_SDA(AckBit); MyI2C_W_SCL(1 ); MyI2C_W_SCL(0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 uint8_t MyI2C_ReceiveAck (void ) { uint8_t AckBit; MyI2C_W_SDA(1 ); MyI2C_W_SCL(1 ); AckBit=MyI2C_R_SDA(); MyI2C_W_SCL(0 ); }
软件I2C读取MPU6050 MPU读写字节封装 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 #define MPU6050_ADDRESS 0xD0 void MPU6050_WriteReg (uint8_t RegAddress,uint8_t Data) { MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveAck(); MyI2C_SendByte(Data); MyI2C_ReceiveAck(); MyI2C_Stop(); } uint8_t MPU6050_ReadReg (uint8_t RegAddress) { uint8_T Data; MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveAck(); MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS|0x01 ); MyI2C_ReceiveAck(); Data=MyI2C_ReceiveByte() MyI2C_SendAck(1 ); MyI2C_Stop(); return Data; }
MPU初始化,获取数据封装 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 { OLED_Init(); MPU6050_Init(); uint8_t ID = MPU6050_ReadReg(0x75 );OLED_ShowHexNum(1 ,1 ,ID,2 ); } { OLED_Init(); MPU6050_Init(); MPU6050_WriteReg(0x6B ,0x00 ); MPU6050_WriteReg(0x19 ,0xAA ); uint8_t ID = MPU6050_ReadReg(0x19 );OLED_ShowHexNum(1 ,1 ,ID,2 ); } void MPU6050_Init (void ) { MyI2C_Init(); MPU6050_WriteReg(0x6B ,0x01 ); MPU6050_WriteReg(0x6C ,0x00 ); MPU6050_WriteReg(0x19 ,0x09 ); MPU6050_WriteReg(0x1A ,0x06 ); MPU6050_WriteReg(0x1B ,0x18 ); MPU6050_WriteReg(0x1C ,0x18 ); } void MPU6050_GetData (int16_t *AccX,int16_t *AccY,int16_t *AccZ,int16_t *GyroX,int16_t *GyroY,int16_t *GyroZ) { uint8_t Data_H,Data_L; Data_H=MPU6050_ReadReg(0x3B ); Data_L=MPU6050_ReadReg(0x3C ); *AccX=(Data_H<<8 )|Data_L; Data_H=MPU6050_ReadReg(0x3D ); Data_L=MPU6050_ReadReg(0x3E ); *AccY=(Data_H<<8 )|Data_L; Data_H=MPU6050_ReadReg(0x3F ); Data_L=MPU6050_ReadReg(0x40 ); *AccZ=(Data_H<<8 )|Data_L; Data_H=MPU6050_ReadReg(0x43 ); Data_L=MPU6050_ReadReg(0x44 ); *AccX=(Data_H<<8 )|Data_L; Data_H=MPU6050_ReadReg(0x45 ); Data_L=MPU6050_ReadReg(0x46 ); *AccY=(Data_H<<8 )|Data_L; Data_H=MPU6050_ReadReg(0x47 ); Data_L=MPU6050_ReadReg(0x48 ); *AccZ=(Data_H<<8 )|Data_L; }
I2C通信外设 (同步时序)(硬件I2C)
由硬件电路自动反转引脚电平
软件只需要写入控制寄存器CR(踩油门,打方向盘),数据寄存器DR(看仪表盘),就可以实现协议了。
为了实时监控时序的状态,软件还得读取状态寄存器SR(观看仪表盘,了解汽车的运行状态)
掌握一主多从,7位地址的I2C(起始条件之后,紧跟的一个字节必须是7位地址+读写位)
主机:拥有主动控制总线的权利。从机:只能在主机允许的情况下,才能控制总线
I2C是高位先行,在发送的时候,最高位先移出去 ,然后是次高位
移位8次,就能发送一个字节
在接收的时候,数据通过GPIO口从右边依次移进来,移8次,一个字节就接收完成了
配置硬件I2C时,两个GPIO口都要配置成复用开漏输出的模式(复用:GPIO口的状态是交由片上外设来控制的。开漏输出:是I2C协议要求的端口配置(即使是开漏输出模式,GPIO口也是可以进行输入的))
主机发送
主机接收
起始,从机地址+读,接收应答 接收数据,发送应答 接收数据,发送应答 ,最后一个数据给非应答,之后终止(这个时序是当前地址读的时序,指定地址读的复合格式这里没有给,需要我们自己组合一下)
硬件I2C读取MPU6050
开启I2C外设和对应GPIO口的时钟
把I2C外设对应的GPIO口初始化为复用开漏模式
使用结构体,对整个I2C进行配置
I2C_Cmd 使能I2C
硬件I2C函数
1 2 3 4 5 6 7 void I2C_GenerateSTART (I2C_TypeDef* I2Cx, FunctionalState NewState) ;void I2C_GenerateSTOP (I2C_TypeDef* I2Cx, FunctionalState NewState) ;void I2C_AcknowledgeConfig (I2C_TypeDef* I2Cx, FunctionalState NewState) ;void I2C_SendData (I2C_TypeDef* I2Cx, uint8_t Data) ;uint8_t I2C_ReceiveData (I2C_TypeDef* I2Cx) ;void I2C_Send7bitAddress (I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction) ;
硬件I2C初始化 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 void MPU6050_Init (void ) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_OD; GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStruct); I2C_InitTypeDef I2C_InitStructure; I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed=50000 ; I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2; I2C_InitStructure.I2C_Mode=I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1=0x00 ; I2C_Init(I2C2,&I2C_InitStructure); I2C_Cmd(I2C2,ENABLE); }
硬件I2C封装函数 写寄存器,指定地址写一个字节的时序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void MPU6050_WriteReg (uint8_t RegAddress,uint8_t Data) { I2C_GenerateSTART(I2C2,ENABLE); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCESS); I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCESS); I2C_SendData(I2C2,RegAddress); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCESS); I2C_SendData(I2C2,Data); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCESS); I2C_GenerateSTOP(I2C2,ENABLE); }
读寄存器的函数,指定地址读寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 uint8_t MPU6050_ReadReg (uint8_t RegAddress) { uint8_t Data; I2C_GenerateSTART(I2C2,ENABLE); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCESS); I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCESS); I2C_SendData(I2C2,RegAddress); while (I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCESS); }
状态机
举例:
freertos
实时操作系统(允许多任务同时运行):freertos,ucos,rt-thread
Freerots编程风格
(int从不使用)(short是16位,long是32位)
任务创建 独立的无法返回的函数是函数
任务栈
栈是单片机在ram中一段连续的空间
为每个任务分独立的栈的内存空间
任务函数
任务控制块
列表和列表项(C语言当中的链表):用于跟踪任务
1和5用于检查完整性
2用于记录列表项数量
3用于记录列表当前索引项
4记录最后一个列表项
3->下一个成员
4->前一个成员
6->指向就绪列表
迷你列表项(有些情况不需要列表项这么全的功能)
1.值
2.指向下一个
3.指向上一个
列表初始化
列表项初始化
列表项插入函数
函数的具体实现过程
任务创建函数
任务就绪列表
调度器:实现任务切换
任务切换
总:任务创建->启动->就绪列表->任务调度->任务切换->
任务创建实践过程
1.初始化硬件
2.创建开始任务(创建任务所需堆栈大小,任务控制块,任务函数)
3.启动任务调度器
main此时就完成了(函数要继续执行下去就从开始任务(开始任务有指定的任务函数)(在开始任务函数这里创建其他的任务子函数)这里)(创建完之后再删除开始任务)
上位机和下位机 上位机: 上位机指可以直接发送操作指令的计算机或单片机,一般提供用户操作交互界面并向用户展示反馈数据。 典型设备类型:电脑,手机,平板,面板,触摸屏
下位机: 下位机指直接与机器相连接的计算机或单片机,一般用于接收和反馈上位机的指令,并且根据指令控制机器执行动作以及从机器传感器读取数据。 典型设备类型:PLC,STM32,51,FPGA,ARM等各类可编程芯片
上位机软件: 用于完成上位机操作交互的软件被定义为“上位机软件”;
上位机给下位机发送控制命令,下位机收到此命令并执行相应的动作。
上位机给下位机发送状态获取命令,下位机收到此命令后调用传感器测量,然后转化为数字信息反馈给上位机。
下位机主动发送状态信息或报警信息给上位机。
为了实现以上过程,上位机和下位机都需要单独编程,都需要专门的开发人员在各自两个平台编写代码。
上位机与下位机关系示意图: