CPU(Central Processing Unit,中央处理器)发展出来三个分枝,一个是DSP(Digital Signal Processing/Processor,数字信号处理),另外两个是MCU(Micro Control Unit,微控制器单元)和MPU(Micro Processor Unit,微处理器单元)。
MCU:MCU集成了片上外围器件;MPU不带外围器件(例如存储器阵列),(MCU)是高度集成的通用结构的处理器,是去除了集成外设的MCU;DSP运算能力强,擅长很多的重复数据运算,而MCU则适合不同信息源的多种数据的处理诊断和运算,(MCU)侧重于控制,速度并不如DSP。
MCU区别于DSP的最大特点在于它的通用性,反映在指令集和寻址模式中。DSP与MCU的结合是DSC。 单片机的基本工作原理程序(命令的) -> 存储到存储器中(留下所分配的地址号) -> 使用时地址被取出 -> 被执行
单片机的几个重要概念总线:用于地址分配的线也较多(存储单元多),这些线被称为地址总线。数据、地址、指令:都是‘0’和‘1’的组成的序列功能复用,功能模块的初始化、驱动。程序的执行过程。开发环境的建立stm32单片机简介Cortex-M3内核标准的ARM架构高性能低电压低功耗电压范围2.0到3.6VI/O电压容限为5Vstm32程序编写基本步骤初始化管脚初始化功能模块编写硬件代码实现功能,调试,修改嵌套中断向量控制器嵌套中断向量控制器(Nested Vector Interrupt Controller,简称NVIC)它为基于Cortex-M3的微控制器提供了标准的中断架构和优秀的中断响应能力,为超过240个中断源提供专门的中断入口,而且可以赋予每个中断源单独的优先级。利用NVIC从可以达到极快的中断响应速度,从收到中断请求到执行中断服务的第一条指令仅需12个周期。这种极快的响应速度一方面得益于Cortex-M3内核对堆栈的自动处理机制,这种机制是通过固化在CPU内部的微代码实现的。另一方面,在中断请求连续出现的情况下,NVIC使用一种称为“尾链”的技术,使连续而来的中断可以在6个时钟周期内得到服务。在中断的压栈阶段,更高优先级的中断可以不耗费任何额外的CPU周期就能完成嵌入低优先级中断的动作。具体的细节后面我会继续总结的。用户可以通过设置CPU自动进入低功耗状态,而使用中断来将其唤醒,CPU在中断时间来临之前会一直保持睡眠状态。
寄存器与存储器的区别一般意义上理解,寄存器是CPU里的存储单元,与CPU离得近,所以CPU在运算时通常都会用寄存器当中转站。存储器是在CPU外部的存储器,分为RAM,ROM。 对单片机来说,因为存储器,CPU都在一个片内,所以寄存器是片内RAM的一部分。
也就是说单片机的存储器包括寄存器
串行通讯计算机通信:通信分为并行通信与串行通信两种基本方式。并行通信:将数据的各位用多条数据线同时进行传送,外加地址线和通信控制线。(多条数据线+地址线+通信控制线)串行通信:将数据分成1位1位的形式在一条传输线上逐个地传送。特殊的串行通讯:4位串行 并行通信和串行通信: 并行通信常用于集成电路芯片的内部、同一插件板上各部件之间、同一机箱内各插件板之间的信息交互 串行通信常用于设备之间的信息交互 同步通信和异步通信 根据数据传输方式的不同,可将串行通信分为同步通信和异步通信串行通信常用于设备之间的信息交互异步通信:若接收端与发送端使用的不是同一时钟信号(但必须同频率),则为异步通信。以字符为单位,一个字符一个字符地传送,并且每一个字符要有起始符和停止符作为开始和结束的标志。(一个字符就是一帧) 。常见的是USART(串口) 同步通信是一种数据连续传输的串行通信方式,通信时发送方把需要发送的多个字节数据和校验信息连接起来,组成数据块。发送时,发送方只需在数据块前插入1~2个特殊的同步字符,然后按特定速率逐位输出(发送)数据块内的各位数据。接收方在接收到特定的同步字符后,也按相同速率接收数据块内的各位数据。(常见的有IIC,SPI等) 串行通讯的工作方式 单工方式:这种方式只允许数据按一个固定的方向传输。数据传输仅能从发送设备传输到接收设备。 半双工方式:数据可以从A发送到B,也可以由B发送到A。但A、B之间只有一根传输线,因此同一时刻只能作一个方向的传送。其传送方向由收发控制开关K切换。平时一般让A、B方都处于接收状态,以便能够随时响应对方的呼叫。两个串行通信设备之间只有一条数据线,数据传输可以沿两个方向,但需要分时进行。全双工方式:数据可同时在两个方向上传送。 串口与RS232、RS485、RS422(属于物理层知识) RS422是差分全双工(4线),RS232是双工(3线),RS485是半双工(2线) 波特率 单位:bps(bit per second) 定义:每秒钟传送的二进制位数。 Baudrate(波特率范围):50 ~ 19200 ~ 57600 50,100,150,300,600,1200,2400, 4800,9600,19200,38400,57600,115200 如每秒传送240个字符,而每个字符格式包含10位这时的波特率为10位(bit)×240个/s = 2400 bit/s。 在异步串行通信中,接收方和发送方应使用相同的波特率,才能成功传送数据。 STM32的串口 一般意义上我们所说的串口指的是USART。SPI,IIC都会特别指出。 至少有3个串口,根据具体的型号有所不同。1号串口可以下载程序用。使用串口首先要使能IO时钟,串口模块时钟,初始化IO,初始化串口。有关串口的高级使用:中断,串口重定向。调试手段设置断点,设置断点的技巧:
在程序运行的代码中可以设置断点,程序运行到断点处即停止,但是断点处不见得就是问题出现的时刻。需要根据猜测设置需要进入的条件。
串口日志输出:在程序运行的过程中可以通过串口将运行过程中的一些关键变量输出以得知程序运行的状态是否正确。
通过外界输入一些信息,影响程序运行的状态,观察运行的输出以程序运行是否正常。中断什么是“中断”?CPU执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行 ,这一过程称为中断。 EG:吃饭时突然手机铃响…
STM32中的优先级概念 STM32(Cortex-M3)中有两个优先级的概念:抢占式优先级和响应优先级,也把响应优先级称作“亚优先级”或“副优先级”,每个中断源都需要被指定这两种优先级。 何为占先式优先级(pre-emption priority)高占先式优先级的中断事件会打断当前的主程序/中断程序运行—抢断式优先响应,俗称中断嵌套。何为副优先级(subpriority)在占先式优先级相同的情况下,高副优先级的中断优先被响应;在占先式优先级相同的情况下,如果有低副优先级中断正在执行,高副优先级的中断要等待已被响应的低副优先级中断执行结束后才能得到响应—非抢断式响应(不能嵌套)。 stm32中对中断优先级的定义 STM32中指定中断优先级的寄存器位有4位,这4个寄存器位的分组方式如下: 第0组:所有4位用于指定响应优先级 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级 第4组:所有4位用于指定抢占式优先级stm32的串口接收中断 步骤一:初始化GPIO 步骤二:开时钟步骤三:初始化USART1步骤四:编写中断函数 void USART1_IRQHandler(void)八段码八段码(数码管)的基本概念 stm32的八段码使用 步骤一:初始化GPIO 步骤二:开时钟步骤三:初始化USART1步骤四:编写中断函数 void USART1_IRQHandler(void)步骤五: 找段步骤六:换码定时器 stm32的定时器 STM32一共有8个通用16位Timer,其中TIMER1和TIMER8是高级定时器,其它的TIMER2~TIMER7是普通定时器。 此外还有一个Systick(系统滴答定时器),这个定时器通常在操作系统中作为系统的任务切换周期。还有一个RTC,是一个毫秒定时器,支持秒级中断,用来做实时时钟计数器。看门狗定时器 也可以算一个。 8个定时器中,Timer1和Timer8是由APB2(输出最高频率为72MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为72MHz。Timer2~Timer7则是由APB1(输出最高频率为36MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为36MHz。 Systick(系统滴答定时器) c //初始化: void Delay_Init(u8 SYSCLK) { SysTick->CTRL &=~BIT(2);//关定时器 SysTick->CTRL &=~BIT(1);//关闭中断请求 fac_us = SYSCLK/8; //计算延时系数 fac_ms = (u16)fac_us*1000; } c //延时函数: void delay_us(u32 nus) { SysTick->LOAD = (u32)fac_us*nus-1;//加载延时参数 SysTick->VAL = 1; //重置计数寄存器(初始化) SysTick->CTRL |= BIT(0); //开定时器(SysTick使能) while(!(SysTick->CTRL&(1<<16)));//等待计数到0(判断是否到零) SysTick->CTRL &=~BIT(0);//关SysTick(关闭) } c //延时函数定义: void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while(TimingDelay != 0); } systick也有中断入口也可以使用中断方式进行延时,具体代码可下自行研究。 stm32的通用定时器 TIM2-TIM5普通定时器的定时功能。 编程步骤配置系统时钟;配置NVIC;配置GPIO;配置TIMER; 配置: (1) 利用TIM_DeInit()函数将Timer设置为默认缺省值;(2) TIM_InternalClockConfig()选择TIMx来设置内部时钟源; //可省略(3) TIM_Perscaler来设置预分频系数;(4) TIM_ClockDivision来设置时钟分割;(5) TIM_CounterMode来设置计数器模式;(6) TIM_Period来设置自动装入的值(7) TIM_ARRPerloadConfig()来设置是否使用预装载缓冲器 //可省略(8) TIM_ITConfig()来开启TIMx的中断其中(3)-(6)步骤中的参数由TIM_TimerBaseInitTypeDef结构体给出。步骤(3)中的预分频系数用来确定TIMx所使用的时钟频率,具体计算方法为:CK_INT/(TIM_Perscaler+1)。CK_INT是内部时钟源的频率,是根据2.1中所描述的APB1的倍频器送出的时钟,TIM_Perscaler是用户设定的预分频系数,其值范围是从0 – 65535。步骤(7)中需要禁止使用预装载缓冲器。当预装载缓冲器被禁止时,写入自动装入的值(TIMx_ARR)的数值会直接传送到对应的影子寄存器;如果使能预加载寄存器,则写入ARR的数值会在更新事件时,才会从预加载寄存器传送到对应的影子寄存器。 stm32的TIM2初始化
```c void TIMER_cfg() { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义timer结构体变量
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM 2-7在总线1上面
//重新将Timer设置为缺省值。。 TIM_DeInit(TIM2);
//采用内部时钟给TIM2提供时钟源, // TIM_InternalClockConfig(TIM2); //源程序 有这个,但是去掉 也无妨
//预分频系数为36000-1,这样计数器时钟为36MHz/36000 = 1kHz TIM_TimeBaseStructure.TIM_Prescaler = 36000 - 1; //设置时钟分割 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置计数器模式为向上计数模式 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数溢出大小,每计2000个数就产生一个更新事件 TIM_TimeBaseStructure.TIM_Period = 2000 - 1; //将配置应用到TIM2中 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
//清除溢出中断标志 TIM_ClearFlag(TIM2, TIM_FLAG_Update); //禁止ARR预装载缓冲器 // TIM_ARRPreloadConfig(TIM2, DISABLE); //开启TIM2的中断 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM2,ENABLE); } ```
stm32的TIM2定时器中断函数```c void NVIC_cfg() { NVIC_InitTypeDef NVIC_InitStructure; //选择中断分组1 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//选择TIM2的中断通道 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //抢占式中断优先级设置为0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //响应式中断优先级设置为0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //使能中断 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //初始化 }
void TIM2_IRQHandler(void) { u8 ReadValue; //检测是否发生溢出更新事件 if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { //清除TIM2的中断待处理位 TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); //将PB.5管脚输出数值写入ReadValue ReadValue = GPIO_ReadOutputDataBit(GPIOD,GPIO_Pin_13); if(ReadValue == 0) { GPIO_SetBits(GPIOD,GPIO_Pin_13); } else { GPIO_ResetBits(GPIOD,GPIO_Pin_13); } } } ```
通讯协议(串口) 通讯协议: 通信协议或简称为传输协议(Communications Protocol)在电信中,是指在任何物理介质中允许两个或多个在传输系统中的终端之间传播信息的系统标准,也是指计算机通信或网络设备的共同语言。[1], 通信协议定义了通信中的语法学, 语义学和同步规则以及可能存在的错误检测与纠正。通信协议在硬件,软件或两者之间皆可实现[2]为了交换大量信息,通信系统使用通用格式(协议)。每条信息都有明确的意义使得预定位置给予响应,并独立生效回馈指定行为,通信协议须参与实体都同意才能生效 通讯协议又称通信规程,是指通信双方对数据传送控制的一种约定。约定中包括对数据格式,同步方式,传送速度,传送步骤,检纠错方式以及控制字符定义等问题做出统一规定,通信双方必须共同遵守,它也叫做链路控制规程。 通讯协议的要素: 包头 包尾地址 解析 通讯协议处理设计思想 命令缓存区 buffer 分帧命令处理硬件pwm 硬件pwm基本原理 pwm由定时器产,定时器的定时时间决定了pwm的频率 比较寄存器决定了pwm的占空比 (比较寄存器,作用是其中寄存了与计数寄存器相比较的值,当两个值相等时,发生一次比较跳变,输出的pwm波跳变 ) 定时器与输出pwm的管脚之间是有约束关系的。 pwm初始化```c //第一步:配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_T IM1,ENABLE); //第二步,配置goio口 GPIO_InitStructure2.GPIO_Pin=GPIO_Pin_8; GPIO_InitStructure2.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure2.GPIO_Mode=GPIO_Mode_Out_PP; //设置为复用浮空输出 GPIO_Init(GPIOA,&GPIO_InitStructure2 GPIO_InitStructure2.GPIO_Pin=GPIO_Pin_13; GPIO_InitStructure2.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure2.GPIO_Mode=GPIO_Mode_Out_PP; //设置为复用浮空输出 GPIO_Init(GPIOB,&GPIO_InitStructure2); //第三步,定时器基本配置 TIM_TimeBaseStructure.TIM_Period=1000-1; // 自动重装载寄存器的值 TIM_TimeBaseStructure.TIM_Prescaler=72-1; // 时钟预分频数 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 采样分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数 TIM_TimeBaseStructure.TIM_RepetitionCounter=0; //重复寄存器,用于自动更新pwm占空比 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
//第四步pwm输出配置 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2; //设置为pwm1输出模式 TIM_OCInitStructure.TIM_Pulse=500; //设置占空比时间 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; //设置输出极性 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //使能该通道输出 //下面几个参数是高级定时器才会用到,通用定时器不用配置 TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_High; //设置互补端输出极性 TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Enable;//使能互补端输出 TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Reset; //死区后输出状态 TIM_OCInitStructure.TIM_OCNIdleState=TIM_OCNIdleState_Reset;//死区后互补端输出状态 TIM_OC1Init(TIM1,&TIM_OCInitStructure); //按照指定参数初始化 //第五步,死区和刹车功能配置,高级定时器才有的,通用定时器不用配置 TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Disable;//运行模式下输出 TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable;//空闲模式下输出选择 TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; //锁定设置 TIM_BDTRInitStructure.TIM_DeadTime = 0x90; //死区时间设置 TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; //刹车功能使能 TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;//刹车输入极性 TIM_BDTRInitStructure.TIM_Auto ** ticOutput = TIM_Auto ** ticOutput_Enable; //自动输出使能 TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); //第六步,使能端的打开 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIMx在CCR1上的预装载寄存器 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器 TIM_Cmd(TIM1,ENABLE); //打开TIM2 //下面这句是高级定时器才有的,输出pwm必须打开 TIM_CtrlPWMOutputs(TIM1, ENABLE); //pwm输出使能,一定要记得打开 // 在运行当中想要改变pwm的频率和占空比调用: TIM_SetAutoreload(TIM1,1000); TIM_SetCompare1(TIM1,500); //下面这句是高级定时器才有的,输出pwm必须打开 TIM_CtrlPWMOutputs(TIM1, ENABLE); //pwm输出使能,一定要记得打开 // 在运行当中想要改变pwm的频率和占空比调用: TIM_SetAutoreload(TIM1,1000); TIM_SetCompare1(TIM1,500);```