导图社区 嵌入式学习
嵌入式学习的思维导图,其中STM32F103C8T6有不同的系列: STM32HXX:性能最好 STM32FXX:主流系列,性能适中 STM32LXX:低功耗系列 STM32WXX:无线系列
编辑于2023-08-18 08:06:42 广东单片机MCU
STM32F103C8T6
不同的系列
STM32HXX:性能最好 STM32FXX:主流系列,性能适中 STM32LXX:低功耗系列 STM32WXX:无线系列
2.0~3.6v供电
片上资源(外设)
引脚定义
I/O:输入或输出 I:输入 O:输出 S:电源
而I/O口和外设有时会共同使用一个引脚,因此对于这样的引脚会有默认的功能和复用的功能。但很多置外设的I/O复用引脚可以通过重映射功能从别的I/O口引出。 从外设的角度看:一个外设的引脚默认从这个口出,但可以重映射到那个I/O口出(用软件实现),但前提是那个I/O口有它这个功能的重定义 从I/O口的角度看:I/O既是默认的复用功能的引脚,也是重定义功能的引脚,只不过要通过软件来激活
调试接口:JTAG、SWD (我们使用的ST-LINK是用SWD)
若调试接口功能的端口被配置成了I/O,就无法使用调试的功能了,就下载不了程序了 只能通过串口来下载
CPU
ARM Cortex-M3 ST公司基于ARM Cortex-M内核(CPU)开发的32位微控制器 (ST公司拿着ARM公司设计的内核,再完善外围电路) ARM公司设计但不生产,只是卖专利给别人,让半导体厂商生产 (半导体知识产权供应商)
早期名字:ARMXX 现在叫:Cortex-XX (R、M系列适用于嵌入式,A系列(性能最好)适用于手机) (R:实时控制、M:微控制)
RAM
SRAM(静态RAM)
20K
运行内存
ROM
Flash
64K
用于存储我们写的程序(程序存储器)
启动配置
系统存储器中存的是一段BootLoader程序,BootLoader就是接收串口的数据,然后刷新到主闪存存储器中,即使用串口下载程序
程序架构
Start
启动文件:startup
定义了中断向量,中断服务函数等 (STM32就是从启动文件开始执行的)
配置时钟:system
配置STM32主频为72MHz
主动执行,在执行main之前就自己执行了的
外设寄存器描述文件
用来描述STM32有哪些寄存器以及他们的地址
内核寄存器描述文件
内核寄存器配置函数
资源,被动使用
Library
标准库函数
System
系统工具函数 一般为用户添加的如Delay等便于使用的函数
User
用户自定义函数
时钟控制器RCC
可开启或关闭各路总线的时钟,在使用各外设功能必须先开启其对应的时钟,没有这个时钟内部的各器件就不能正常运行。
RCC时钟树
内部震荡源(晶振): 8MHz HSI RC:内部8MHz高速RC振荡器 4-16MHz HSE OSC:外部4-16MHz高速石英晶体振荡器(一般为8MHz) 32.768kHz LSE OSC:外部32.768MHz低速晶振(一般为RTC提供时钟) 40kHz LSI RC:内部40kHz低速RC振荡器(可以给看门狗提供时钟) (外部的石英晶体振荡器比内部的RC振荡器更稳定,但外部晶振需要额外电路) (单片机内部是没有晶振的,只有RC振荡器,只是晶振的输出连接进了单片机中作为了单片机的内部时钟)
两个高速晶振用于系统时钟: 首先是先使用内部RC的8MHz作为系统频率,然后将外部石英晶振时钟8MHz经过锁相环倍频9倍得到72MHz,等锁相环稳定后再将72MHz输出为系统时钟 (内部和外部都是8MHz,但是外部时钟经过了9倍频)
CSS:检测外部时钟状态,若外部时钟坏了则切换为使用内部时钟,防止程序卡死造成事故
定时器的时钟频率统一默认为72MHz(即使分频后也会进行判断然后再倍频回72MHz)
外设时钟使能:对应函数RCC_XXXPeriphClockCmd,来启动对应外设的时钟(XXX可以为APB1、APB2等等外设总线,然后再对应到具体外设)
由systemInit函数进行执行配置
GPIO
通用输入输出口(Genneral Purpose Input Output)
引脚电平:0~3V,部分可以容忍5V
GPIO挂载在APB2总线上
GPIO有很多个,以GPIOx来命名,x为A~E(5个)
每个GPIO有16个引脚
在使用GPIO前需要通过时钟控制器开启APB2上的GPIO外设的时钟,GPIO才能使用 (对应寄存器:RCC_APB2ENR)
GPIO基本结构
每个GPI/O端口有: 两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH), 两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR), 一个32位置位/复位寄存器(GPIOx_BSRR), 一个16位复位寄存器(GPIOx_BRR)和 一个32位锁定寄存器(GPIOx_LCKR)
端口配置
GPIO的8种模式选择
(输入模式时不能输出) 浮空输入:上拉和下拉开关都断开 上拉输入:上拉开关闭合,下拉开关断开 下拉输入:下拉开关闭合,上拉开关断开 模拟输入:GPIO无效(开关悬空,触发器关闭),引脚直接接入内部ADC (输出模式时可以输入) 开漏输出:P-MOS无效。高电平:高阻态(无驱动能力),低电平:vss (可用于I2C或外加更高的电源上拉以满足更高电压输出的需求(5V)) 推挽输出:P-MOS和N-MOS都有效。高电平:vcc,低电平:vss 复用开漏输出:和开漏输出一样,只不过引脚控制权给到片上外设来控制 复用推挽输出,和推挽输出一样,只不过引脚控制权给到片上外设来控制
上拉下拉开关:设置当引脚浮空时,默认为高电平或低电平还是浮空(电平易受外界影响) (弱上拉、弱下拉,为了不影响正常的输入操作,只是让引脚悬空的时候能有个默认值) 施密特触发器(大于某一阈值就输出高电平,低于某一阈值就输出低电平):用于波形整型 输入数据寄存器: 输出数据寄存器:写这个寄存器的某一位就能操作对应端口(只能整体对16个端口一起进行读写) 位设置/清除寄存器:单独操作是输出数据寄存器的某一位而不影响其他位 P-MOS、N-MOS:寄存器数据为1时连接输出和VDD、寄存器数据为0时连接输出和VSS (VDD、VSS具有较强的驱动能力)
端口配置高寄存器、端口配置低寄存器 GPIOx_CRH、GPIOx_CRL
共A~E组(x的取值范围),一组内有16个端口,分为高8个,低8个进行配置
端口输入数据
端口输入寄存器 GPIOx_IDR
低16位对应16端口,高16位不使用
端口输出数据
端口输出寄存器 GPIOx_ODR
低16位对应16端口,高16位不使用
单独对某一端口进行设置
端口位设置/清除寄存器(低16设置,高16位清除) GPIOx_BSRR
同时对多个端口进行位设置和位清除
端口位清除寄存器(低16位) GPIOx_BRR
单独对位清除,且也是低16位,和设置一一对应,操作起来更方便
对端口配置进行锁定,防止意外更改
GPIOx_LCKR
标准库函数
主要使用的几个函数: void GPIO_Init(GPIO_TypeDef* GPIOx, 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); (具体功能查手册!)
AFIO
AFIO复用IO口(数据选择器)
主要用于引脚复用功能的选择和定义,而引脚重映射需要看手册里已经确定的重映射方式,不是想重映射那个引脚就重映射哪个引脚。 (在STM32中,AFIO主要完成:复用功能引脚重映射、中断引脚选择这两个功能)
标准库函数
void GPIO_AFIODeInit(void); //清除AFIO的配置 void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //锁定AFIO的配置,防止意外更改 void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); //配置AFIO的事件输出功能 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); //配置外部中断的引脚
中断系统
68个中断源,包含:EXIT、TIM、ADC、USART、SPI、I2C、RTC等多个外设 使用NVIC(内核外设)统一管理中断,分配优先级,安排各个中断的顺序,让CPU能够专心工作,而不需要被处理中断顺序所影响
抢占优先级:进行中断嵌套,直接响应
响应优先级:插队,但还是要等
值越小,优先级越高
当两个中断发生冲突时,就对比他们这连个优先级, 抢占优先级高的就直接响应。 响应优先级高的就插队等待。 若同时到达且抢占优先级一样,则看响应优先级谁高就先执行谁。
外部中断EXTI
支持的触发方式:上升沿、下降沿、双边沿、软件触发 (软件触发:引脚无变化,程序里执行一句代码,就能触发中断)
支持的GPIO口:所有的GPIO口都可以当作外部中断的引脚,但相同的Pin不能同时触发中断 (例:PA1和PB1,所以需要多个中断时要用PA1和PA2或者PD4和PB3这样子)(所以最多16个)
(占用的)通道数(20个):16个GPIO_Pin。外加(蹭网):PVD输出、RTC闹钟、USB唤醒、以太网唤醒(因为外部中断有个功能:能从低功耗模式的停止模式唤醒STM32)
触发响应方式:中断响应(正常的触发中断,执行中断函数) 事件响应(不触发中断,外部中断不通向CPU而是通向其他外设,来触发其他外设的操作)
EXTI内部框图 请求挂起寄存器:相当于中断标志位,用于区分是哪一个中断
标准库函数
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); //获取指定的标志位是否被置1了 void EXTI_ClearFlag(uint32_t EXTI_Line); //对指定的标志位进行清除 ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); //获取中断标志位是否被置1了 void EXTI_ClearITPendingBit(uint32_t EXTI_Line); //清除中断挂起标志位
EXTI9_5、EXTI15_10要通过标志位进一步选取是哪一个中断进来的
NVIC中断控制器 (优先级管理)
32内核的设备,用于分配各个中断的优先级顺序
标准库函数
(常用) void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //配置优先级分组 void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); //先配置对应中断的优先级,NVIC_InitTypeDe类型的结构体内包含了对应中断的信息
TIM定时器
STM32F103只有TIM1~4
定时器的一个时基单元包括:(都是16位的) (CNT)计数器:用于计数,每过一个时钟加1 (CK_PSC)预分频器:用于将时钟频率进行分频 (ARR)自动重装寄存器:设定计数器目标值,当计数器到达这个值时清理并申请中断
CK_CNT=CK_PSC/(PSC+1) CK_CNT_OV(溢出频率)=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1) 所以,多少秒溢出一次(多久进一次中断)=(ARR+1)/CK_CNT=(PSC+1)(ARR+1)/CK_PSC
基本定时器
预分频器输入端(时钟源)来自内部时钟,由RCC负责配置与分配的频率(一般为系统主频72MHz) 输出端将会对输入的频率进行分频,当PSC为0时,不分频(1分频),PSC为1时,2分频(72MHz->36MHz) (由于有16位,所以最大可以65536分频)
图中预分频器右下方有阴影,说明具有缓存机制: 即,当我们输入分频参数后不会直接改变分频系数,而是等待这个时间周期结束后影子寄存器才获取新的分频系数并改变分频系数
预分频器其实也是靠计数器来分频的,例: 当PSC为0时,00000000(0分频,频率为8) 当PSC为1时,01010101(1分频,频率为4) 当PSC为3时,01230123(3分频,频率为2)
标准库函数:(常用) void TIM_InternalClockConfig(TIM_TypeDef* TIMx); //将定时器设置为使用内部时钟来驱动 void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); //定时器时基初始化 void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG); //清除定时器中断标志位 void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); //使能定时器中断 void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); //使能定时器
计数器到达自动重装载寄存器内设定的值后就会产生中断:“更新中断” UI:“更新中断”产生后会通往NVIC到COU内进行响应 U:“更新事件”不会触发中断,但是可以触发内部其他电路的工作
计数器有预装时序:自动重载寄存器的值更新后不会改变重装值,而是等到这一次的计数周期结束后影子寄存器获取新的重装值并改变装入 计数器无预装时序:自动重载寄存器的值更新后直接写入,此时计数器到达这个之后就会清0并申请中断
主模式触发DAC(数模转换器):将“更新事件”映射到TRGO来去触发DAC,而不需要通过中断来调用中断函数去触发DAC (内部的硬件在不受程序的控制下自动运行)
通用定时器
支持向上、向下、中央对齐的计数模式 向上计数模式:计数器从0开始加到一定值产生中断 向下计数模式:计数器从65535(最大值)减到一定值产生中断 中央对其计数模式:计数器从0加到一定值产生中断,再减到时也产生中断
时钟源不仅可以选择内部时钟72MHz,还可以选择外部时钟: ETR:外部时钟模式2(正常的接外部时钟) TRGI:外部时钟模式1
主模式:可通过TRGO输出连接到其他定时器的ITRx上,实现定时器级联 从模式:ITRx可被其他的定时器的TRGO连接,实现定时器级联 (32内部已连好,可以查表获得)
TI1F_ED:连接的是输入捕获相关的引脚TIMx_CHx(外接时钟,我们来捕获) ED:边沿的意思,意思是上升沿也可以下降沿均有效(均可以检测到)(经过了边沿检测器)
TI1FP1、TI2FP2:可分去用于做编码器接口,可以读取正交编码器的输出波形
标准库函数:(常用) void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); //设置定时器使用通过ETR引脚的外部时钟模式2
TIMx_CHy:输出比较或输入捕获(共用同一个引脚、寄存器(捕获/比较x寄存器)) 输入捕获:可用于测输入的方波的频率 输出比较:可用于输出PWM波形驱动电机(CCR:捕获/比较寄存器) 这里只给出了TI1FP1这一条电路,实际上应该还有一条一样的电路是T1FP2,他连接到通道2
OC输出比较
输出比较可以通过比较CNT(计数器的值)和CCR(捕获/比较寄存器)的值的关系,来对输出电平进行置1、置0或反转的操作,用于输出一定频率和占空比的PWM波形
PWM模式: PWM频率=CK_PSC/(PSC+1)/(ARR+1)(计数器的更新频率) PWM占空比=CCR/(ARR+1) PWM分辨率=1/(ARR+1)(每次能改变占空比的最小步长) (一般将为了方便计算将ARR+1设置为了100,这样CCR等于多少,占空比就为多少)
标准库函数:(常用) void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); //输出比较初始化 void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1); //单独修改CCR的值
IC输入捕获
在输入捕获的模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
将信号滤波(减少毛刺)之后,进行边沿检测,当出现指定电平跳变时,将当前CNT的值存入CCR中,然后读取CCR的值就可以知道CNT计数了多少次,由于CNT计数的频率我们是知道的,所以我们就可以得出两次相同的电平变化间隔的时间,也就是波形的周期(测周法) CNT在每次将值存入CCR之后应该清零,这样CCR每次得到的值才都是直观好计算时间的,所以要求每次出现指定电平跳变的时候需要将CNT清零。而在输入捕获模式下检测到指定电平跳变后还会触发从模式控制器,这个从模式里面就有电路可以自动完成CNT的清零 测量信号的占空比只需要在两次上升沿之前,记录一下下降沿时CNT的值,就可以得到高电平持续的时间(脉宽),然后除以周期就可以得到占空比了
通道1和通道2是一组,他们可以交叉连接,而且边沿检测的极性选择是通道1和通道2共用一个(配置同一个寄存器),所以可以在配置通道2的时候将信号源设置为通道1(交叉通道),且修改捕获的边沿极性
编码器接口
编码器接口可接受增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
使用通道一和通道二作为两个正交信号的输入,后续的对信号的判断,计数器的自增和自减都由硬件完成,我们只需要配置寄存器即可 每检测到一个一次规定的边沿后,就会根据规则得出是正转还是反转,然后对计数器进行加1或减1,在单位时间内可以得到计数器的值从而得到频率(测频法)
高级定时器
重复次数计数器:每隔几次计数周期,才发生1次“更新事件”或“更新中断”(可看作是进行了分频)
DTG:死区生成电路 为了驱动三相无刷电机,需要三个推挽输出电路,而推挽输出电路需要两个互补的引脚来控制,所以原来的输出引脚由原来的一个变成了两个互补的输出,可以输出一对互补的PWM波(用于驱动三相无刷电机的) 推挽电路:由2个mos管组成,当上面的mos管导通时,下面的mos管需要关闭,此时接到VCC输出高电平(推),当下面的mos管导通时,上面的mos管需要关闭,此时接到VDD输出低电平(挽)(抽取电流),上下两个mos管分别由两个引脚控制,在开关(mos管)切换的瞬间,由于器件的不理想,造成短暂的直通现象,所以前面加上了死区生成电路,在开关切换的瞬间产生一定时长的死区,让桥臂的上下管全部关上,防止直通现象。 (由于三相无刷电机只需要三路,所以CH4没有变成两路,还是和原来一样)
标准库函数:(常用) void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState); //使能主输出,使用高级定时器输出PWM特有
刹车输入功能:为了给电机驱动提供安全保障 如果TIMx_BKIN产生了刹车信号,或者内部时钟失效,产生了故障,那么控制电路就会自动切断电机的输出,防止意外的发生
ADC
模拟-数字转换器 18个输入通道,可测量16个外部和2个内部信号源 12位逐次逼近型ADC,1us转换时间 有规则组和诸注入组两个转换单元 模拟看门狗自动监测输入电压范围 高级功能:双ADC模式
逐次逼近型ADC(图为ADC0809) 内部有一个DAC,通过DAC生成对应数字量的模拟量和输入电压值进行比较,利用二分法找到和输入电压最接近模拟量,从而输出该模拟量对应的数字量 VREF(+/-)为参考电压,他决定了模拟量的变化范围(数字量的变化范围由ADC的位数决定),一般VREF都是接在VCC和GND上的 (二分法里的除2刚好对应二进制里右移一位,所以很方便计算) START:开始转换,给一个输入脉冲就说明可以开始转换了 CLOCK:二分法需要一次次判断,所以要一个时钟 地址锁存和译码:用来选择输入通道的,ALE:锁存信号,相当于选择好通道后的确定键(当需要转换多个数据时可以通过选择输入来实现一个接着一个完成)
数据寄存器:用于存放转换结果 数据对齐:右对齐,高位多出来的补0(ADC是12位的,但是数据寄存器是16位的) (左对齐的作用:降低ADC分辨率,左对齐后只取高8位,就可以舍弃后面4位的精度)(可以提高转换速度)
注入通道:最多选择4个通道,相当于一次把4个要AD转换的值都传进去,注入通道数据寄存器有4×16位,可以放4个转换后的数,无需考虑数据会被覆盖的问题 规则通道:最多选择16个通道,相当于一次把16个要AD转换的值都传进去,但规则通道数据寄存器只有16位,也就是只能放一个转换后的数,所以需要DMA将转换后的数据先转移出去,否则会被后面的数据覆盖掉
开始触发(START):1、软件触发,调用一条代码 2、硬件触发,使用一些触发源(主要来自于定时器)
ADCCLK(CLOCK):驱动内部逐次比较的时钟,来自ADC预分频器,而ADC预分频器来自于RCC(一次脉冲为一个ADC周期)
DMA:用于数据转运的,将寄存器数据转移到别处,防止被覆盖
模拟看门狗:可以设置阈值高限和阈值底限,一旦超出范围后就会申请一个模拟看门狗的中断AWD
标志位:一旦转换完成后数据寄存器也会有一个转换完成的信号EOC/JEOC(规则+注入组/注入组),这个信号可以取到NVIC申请中断
转换时间:STM32 ADC的总转换时间 = 采样时间 + 12.5个ADC周期(12位ADC,二分法需要对12个位都进行判断,所以是12个周期+0.5其他时间=12.5周期) 采样时间:由于输入的模拟量会不稳定,所以需要对这个模拟量进行一定时间的采样,取得一个确定值,然后才能进行AD转换。(也是使用ADCCLK) ADC周期:由ADCCLOCK决定,每次判断大小的时间 (采样时间会在通道选择的时候配置,每个通道的采样时间都可以不同)
校准:ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。 建议在每次上电后执行一次校准 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期 (只需在初始化最后加几个函数就可以了)
规则通道的4种转换模式: 1、单次转换,非扫描模式:只使用第一个序列,将要转换的通道写在第一个序列上,然后触发转换,当转换完成后输出(EOC)转换完成信号,然后再在数据寄存器中提取结果。 若要改变转换的通道,需要手动修改序列1上的通道号 若要再进行一次转换,则要再触发一次 2、连续转换,非扫描模式:与单次转换不同的是,他在转换完一次之后不会停止,而是立刻开始下一轮的转换,一直持续下去,所以不需要判断是否结束,想要转换数据时直接从数据寄存器中取就可以了 若一个通道上的的输入变化的话,输出也会实时变化 3、单次转换,扫描模式:非扫描模式不同的是,使用多个序列,可自定义每个序列上放哪个通道,用多少个序列,然后依次对前这么多个序列的通道进行转换,转换结果都会放到数据寄存器中,所以需要AWD转存走,否则会被覆盖 4、连续转换,扫描模式:和2同理 (扫描下还有个间断模式:连续扫描几个来回后,需要重新触发转换一次)
双ADC模式:ADC1、ADC2一起工作,可以组成同步模式和交叉模式等,以提高ADC转换的速度和效率
STM32F103C8T6只有: ADC1、ADC2两个ADC且只有10个外部输入通道,其他的通道没有引脚引出来
标准库: void RCC_ADCCLKConfig(uint32_t RCC_PCLK2); //为ADCCLODK分配时钟,为APB2时钟的几分频 void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); //选择输入通道,各自的顺序以及各自的采样时间 void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct); //配置ADC的模式以及再扫描模式下要扫描的通道数 void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); //使能ADC void ADC_ResetCalibration(ADC_TypeDef* ADCx); //开始ADC复位校准 FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx); //获取复位标志位,复位完成后复位标志位被硬件自动置0 void ADC_StartCalibration(ADC_TypeDef* ADCx); //开始ADC校准 FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx); //获取校准标志位,校准完成后校准标志位被硬件自动置0 void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); //软件触发ADC转换 FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); //获取ADC转换标志位,可选择EOC、JEOC、AWD等等 uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx); //获取ADC转换的结果(读数据寄存器)
DMA
直接存储器存取
DMA可以提供外设(寄存器)和存储器或者存储器和存储器之间的高速传输,无需CPU干预,节省CUP资源。 即DMA可以直接访问STM32的存储器(包括SRAM、Flash等等) 将一个站点的数据复制到另一个站点内
主要是配合ADC扫描模式的使用,因为ADC扫描模式的结果会不断覆盖掉,所以为了正常使用,就要使用DMA及时的将转换的结果转运出去。 (解决了ADC扫描模式的这个固有的缺陷)
STM32一般有12个可独立配置的通道:DMA1(7个通道)、DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发(每个通道的硬件触发源都是不同的(查手册)) STM32F103C8T6只有DMA1(7通道)
两个站点只是名字叫这个,起始地址里给什么DMA就会去哪里取、放到哪里。没有限制(Flash只读,不能作为写入的对象。但可以通过Flash接口控制器对Flash进行按页读写操作) 数据宽度:uint8_t(字节)、uint16_t(半字)、uint32_t(字) 硬件触发一般用在外设上,因为外设的数据出现时间不定,需要有触发源告诉它数据已经准备好了,可以取走了 软件触发是以最快的速度连续不断的触发DMA转运,争取用最快的速度将数据转运完成。不是执行一次转运一次。 自动重装器的值只能在关闭DMA的时候进行配置
USART通信协议
通用同步/异步收发器
同步通信,发送的时候发送时钟信号来使得双方的时钟同步,但只能USART发送时钟不能接受所以不能做到双向同步收发 异步通信,所以要双方都约定好波特率
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里 发送数据寄存器TDR和接受数据寄存器RDR是同一个寄存器 发送控制器和接收控制器会通过看状态寄存器SR里的TXE(发送寄存器为空,下个数据可以进来了)和RXNE(接收不为空,这个数据可以被取走了)
常用电平标准: TTL:+3.3V或+5V表示1,0V表示0 RS232电平:-3~-15V表示1,+3~+15V表示0 RS485电平:两线电压差+2~+6V表示1,-2~-6V表示0(差分信号)
支持同步模式(同步通信)、硬件流控制(可以控制流量使得接收方能来得及接受)、DMA(转运数据)、智能卡、IrDA、LIN(兼容其他通信协议)
STM32F103C8T6有:USART1(APB2)、 USART2、 USART3(后面两个是挂载在APB1)
I2C通信
子主题
子主题
系统结构
操作系统
之前的51和32都是在进行裸机开发,没有使用操作系统
Linux操作系统
来源
由UNIX演变而来
内核(www.kernel.org)
各个厂商在这个内核的基础上修改添加其他功能,得到Linux发行版
redhat系列
CentOS等
debian系列
ubuntu等
区别:软件安装方式不同
主要应用领域
企业服务器
嵌入式
电脑
CPU
RAM
DRAM(动态RAM)
内存条
ROM
Flash
固态硬盘
硬盘(靠磁介质来存储的)
机械硬盘
手机
RAM
DRAM(动态RAM)
运行内存
ROM
Flash
内存
加上了软件与外围硬件的单片机
RTOS操作系统
μC/OS
FreeRTOS
STC89C52
5v供电
CPU
Intel公司设计的内核 (8位)处理数据
RAM
随机存储器(存储速度快)
掉电丢失
SRAM(静态RAM)
通过D触发器(电路)来存储数据
DRAM(动态RAM)
通过电容充电来存取数据 但是电容容易漏电,所以需要扫描电路不断的补上电
ROM
程序存储器、只读存储器(flash)(存储速度慢)
掉电不丢失
Mask ROM(掩膜ROM)
依靠电路存储数据,生产出来后不可修改
生产时根据客户需要,在需要的地方直接导通(1)或加二极管(0)
PROM(可编程ROM)
可以写入一次,写完后就不可以修改了
生产时在所有地方都放上双向二极管实现断路(0),但有一个二极管是较易被击穿的,通过编程给高电压使其中一个被击穿来实现通路(1)(保险丝也可以)
EPROM(可擦除可编程ROM)
可编写也和擦除,通过用紫外线照射30min来擦除
在PROM的基础上用一种材料断开后通过紫外线照射又可以恢复(断开变为接上了)
E2PROM(电可擦除可编程ROM)(EEPROM)(E方PROM)
可编写也和擦除,利用5v低压电几ms就可以擦除
Flash(闪存)
硬盘、软盘、光盘等
寄存器是连接软硬件的媒介 单片机通过配置寄存器来配置电路
定时器/计数器
在单片机内部的
与Delay的区别:Delay是通过占用cpu的资源实现延时的,而定时器是令cpu跳脱出来,过一会再叫他回来,从而实现延时。中间这段时间cpu可以去做其他的事,不占用cpu资源,提高了cup运行效率
可实现多应用同时运行(时分复用)
T0、T1、T2
模式选择以及参数设置需要配置对应各个寄存器的位
TDOM
TCOM
TL0/1、TH0/1
IO口
P1、P2、P3等等
只能作为单个的0、1输入或输出,和串口不同
中断系统
各个中断
外部中断3(INT3)
外部中断2(INT2)
定时器2中断
串口中断(UART)
定时器1中断
外部中断(INT1)
定时器0中断
外部中断(INT0)
中断寄存器
IE
IP、IPH
XICON(辅助中断控制寄存器)
中断等级有4级(IP和IPH一起,老版本是只有IP,因此只有2级),需要用对应寄存器去配置,否则就用默认的
UART串口通信
电平标准
TTL电平:0:0v,1:5v
RS232电平:0:3~15v,1:-3~-15v
(可传几米)
RS485电平:两线压差0:-2~-6v,1:2~6v(可传几千米)
硬件
VCC
为模块供电(如果模块是有独立电源的就可不连)
GND
0电位基准
TXD
发送口
RXD
接收端
(电压是看相对于GND的)
必接
工作模式
异步,全双工、1对1
有四种工作模式: 模式0: 模式1:8位UART,波特率可变 模式2: 模式3: (由SCON里的SM0、SM1来选择)
通过定时器1来确定速率 波特率:串口通信的速率(发送和接收各数据位的间隔时间,发的速度和收的速率要一样,才能正确接收)
相关寄存器
SCON
SBUF
串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接受寄存器。
PCON
I2C总线(通信协议)
同步、半双工、带数据应答,可挂载多个设备(一对多或多对多) 选择哪一个从机设备的方式: 主机发送的第一个字节是从机设备的地址信息,每个从机都将这个地址信息与自己的地址信息进行比对,若和自己的一样那么就知道主机接下来要通信的从机是我,若不一样则之后的消息就不予以理会(除非开始重新选择从机) 在选择完从机之后可以再发一个起始位来指定想要获取从机的哪个寄存器的数据。
两根通信线: SCL(同步时钟线)、SDA
所有I2C设备的SCL连在一起、SDA连在一起
设备的SCL和SDA均要配置成开漏输出模式(0的时候接地,1的时候悬空(断开),即要1的时候不动,要0的时候下拉)这样就可以实现当一个设备在通信时不受其他设备的影响(数据一直都是1,有人需要发数据时,才可以控制它哪几次变为0)
设置成开漏输出的原因:因为主机和从机都要使用数据线来进行收发,若主机和从机都设置为推挽输出,如果发送数据端切换的时间不够准确,就会发生主机和从机都在输出的情况,若刚好主机输出1,从机输出0,那么就相当于vcc接到gnd了,就是短路了,所以一定要避免这个情况
SCL和SDA各添加一个上拉电阻,阻值一般为4.7kΩ左右
时序结构
起始条件:SCL高电平期间,SDA从高电平切换到低电平
终制条件:SCL高电平期间,SDA从低电平切换到高电平
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机在SCL拉高期间读取数据位(SCL高电平期间SDA不允许有数据变化),一次循环8次后即可发送一个字节
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机在SCL拉高期间读取数据位(SCL高电平期间SDA不允许有数据变化),一次循环8次后即可发送一个字节(主机在接收之前,需要释放SDA)
发送应答(从机发送):在主机接收完一个字节后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。 若发送非应答则表示主机不想要再接收数据了,此时从机就会释放SDA交还给主机。 若发送应答则表示主机还想要接收数据,从机则会继续发送数据。(此时从机发送的数据的寄存器地址+1)
接收应答(主机发送):在从机发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答 (主机在接收之前,需要释放SDA)
数据帧
S:发送、R:接收 地址位:A6~A3固定(厂商提供),A1~A0自己配置 R/W:选择是发送还是接收
复合帧(先发送,再接收) 发送数据:从机地址(写数据),指定从机的哪一个寄存器(设置当前地址指针)+接收数据:从机地址(发数据),要发送的数据
(1-Wire)单总线协议
一根数据线,因为是一根线,上电后默认是主机发信息,从机接收到后就会向主机发送信息,此时主机以及自动将总线控制权交给从机并准备接收信息了
时序结构
初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会找机会拉低总线60~240us以响应主机,之后从机将释放总线
发送一位数据:主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us
接收一位数据:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1,整个时间片应大于60us
SPI(通信协议)
可以1对多
四根数据线
CS
每个挂载的设备单独用一个CS,接到主机上,主机要跟谁通信,就将谁的CS置0
DCLK
时序,上升沿输入,下降沿输出
DIN
子设备的输入
DOUT
子设备的输出
所有子设备和主机共用
时序
发送的第一个字节为模式选择
汇编
常用汇编指令 GUN汇编语言
可以小写可以大写,但要么全部小写要么全部大写
数据传输指令:
MOV 寄存器 寄存器 MOV R0,R1:将R1寄存器里面的数据复制到R0寄存器内(R1的位置可以是立即数,如#0X12)
MRS R0,CPSR:将特殊寄存器CPSR里面的数据复制到R0中
MSR R1,CPSR:将R1里面的数据复制到特殊寄存器CPSR里
存储器访问指令:
在汇编下操作的寄存器是内核寄存器组(R0~R7那些),所以是无法直接配置芯片的外设寄存器的,因此: 要想在汇编下配置外设寄存器,首先你要知道这个寄存器在内存上哪个地址,将这个地址保存下来(R0),我们要先将要赋的值放在某一内核的寄存器(R1)里,然后使用str将内核的寄存器的值赋到(R0)存储的地址
LDR 寄存器,内存 LDR Rn,0x56000054:将0x56000054地址的值存放到Rn寄存器中 LDR Rn,=0x56000054:将这个地址写到Rn中,即Rn=0x56000054 LDR Rd,[Rn]:从D地址为Rn的值的位置读取数据放在Rd 将寄存器用中括号括起来才能读取它的值作为地址
STR Rd,[Rn]:将Rd的值放入地址为Rn的值的存储单元中去 STR和LDR都只有第二个参数可以用[],所以才需要STR将数据写进地址里
压栈和出栈指令:
POP:将寄存器列表存入栈中
PUSH:从栈中恢复寄存器列表
跳转指令:
B+函数名:将PC寄存器的值设置为跳转目标地址,且调用回原来的函数不会从跳转出去的位置开始
BX
BL+函数名:在跳转之前会在LR寄存器中保存当前PC寄存器的值,所以可以通过将LR寄存器中的值重新加载到PC寄存器中来继续从跳转之前的代码处运行。
BXL
算数运算指令
逻辑运算指令