STM32

STM32是STMicroelectronics(意法半导体)推出的一系列基于ARM Cortex-M内核的32位微控制器(MCU)产品。这些微控制器提供了广泛的产品系列,覆盖了多种不同的性能和功能需求,适用于各种应用领域,包括工业控制、汽车电子、消费类电子、医疗设备等。

STM32系列微控制器以其高性能、低功耗、丰富的外设接口和灵活的开发工具而闻名。它们通常具有丰富的存储器、多种通信接口(如UART、SPI、I2C、CAN等)、模拟数字转换器(ADC)、定时器、PWM输出等功能,以满足不同应用场景下的需求。

STM32微控制器通常使用标准的ARM Cortex-M内核,包括Cortex-M0、M0+、M3、M4和M7等,这些内核具有不同的性能和功耗特性,可根据具体应用的需求进行选择。此外,STM32系列还提供了多种封装和引脚配置,以满足不同尺寸和集成度的要求。

STMicroelectronics为STM32系列提供了丰富的开发工具和支持资源,包括基于ARM开发环境的集成开发环境(IDE)、调试器、评估板和参考设计等。这些工具和资源有助于开发人员快速开发和部署他们的应用,并提供了全面的技术支持和文档资料,帮助用户充分发挥STM32微控制器的性能和功能优势。

SPI是一种高速的,全双工同步的通信总线,在芯片管脚上占用了四根线,节约了芯片的管脚,同时为PCB的布局节省了空间,提供了方便,因此越来越多的芯片集成了这种通信协议,STM32也就有了SPI接口。

stm32 SPI介绍和配置

有上图可知有四个通信口,两个位移寄存器是同步的,那MISO和MOSI就不难理解了。

SCLK时钟信号,由主设备产生。CS从设备片选信号,由主设备控制。

1、配置相关引脚的复用功能,使能SPI2时钟。

假设我们要使用SPI2,第一步SPI2时钟使能,第二步相关引脚的输出模式(MISO,MOSI,SCLK,(CS没有接外设的话,我们使用软件管理方式))。

GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//PORTB 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );//SPI2 时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIOB

2、初始化SPI2,设置SPI2工作模式

SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工,还有半双工以及串行发和串行收方式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI 还有副 SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收8或者16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平或者低电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第一个或者第二个跳变沿数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器

3、使能SPI2

初始化完成之后我们就要使能SPI2通信了。

SPI_Cmd(SPI2,ENABLE);//使能SPI外设

4.SPI传输数据

传输数据时,就需要有发送数据和接收数据的函数

发送数据函数为:void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

接收数据函数为:uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

5、查看SPI传输状态

判断数据是否传输完成

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);

转自:zengsf

围观 522

一. TIMER分类:

STM32中一共有11个定时器,其中TIM6、TIM7是基本定时器;TIM2、TIM3、TIM4、TIM5是通用定时器;TIM1和TIM8是高级定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。其中系统嘀嗒定时器是前文中所描述的SysTick。

定时器

计数器分辨率

计数器类型

预分频系数

产生DMA请求

捕获/比较通道

互补输出

TIM1

TIM8

16位

向上,向下,向上/向下

1-65536之间的任意数

可以

4

TIM2

TIM3

TIM4

TIM5

16位

向上,向下,向上/向下

1-65536之间的任意数

可以

4

没有

TIM6

TIM7

16位

向上

1-65536之间的任意数

可以

0

没有

其中TIM1和TIM8是能够产生3对PWM互补输出,常用于三相电机的驱动,时钟由APB2的输出产生。TIM2-TIM5是普通定时器,TIM6和TIM7是基本定时器,其时钟由APB1输出产生。

二、PWM波形产生的原理:

产生波形原理来源:http://www.ndiy.cn/thread-31081-1-1.html
通用定时器可以利用GPIO引脚进行脉冲输出,在配置为比较输出、PWM输出功能时,捕获/比较寄存器TIMx_CCR被用作比较功能,下面把它简称为比较寄存器。
这里直接举例说明定时器的PWM输出工作过程:若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR被配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。
而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。
如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为 A/(N+1) 。

三、STM32产生PWM的配置方法:

1、配置GPIO口:

配置IO口的时候无非就是开启时钟,然后选择引脚、模式、速率,最后就是用结构体初始化。不过在32上,不是每一个IO引脚都可以直接使用于PWM输出,因为在硬件上已经规定了用某些引脚来连接PWM的输出口。下面是定时器的引脚重映像,其实就是引脚的复用功能选择:

a.定时器1的引脚复用功能映像:

STM32之PWM波形输出配置总结

b.定时器2的引脚复用功能映像:
STM32之PWM波形输出配置总结

c.定时器3的引脚复用功能映像:
STM32之PWM波形输出配置总结

d.定时器4的引脚复用功能映像:
STM32之PWM波形输出配置总结

根据以上重映像表,我们使用定时器3的通道2作为PWM的输出引脚,所以需要对PB5引脚进行配置,对IO口操作代码:

GPIO_InitTypeDef GPIO_InitStructure;//定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);//使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //选择Timer3部分重映像
//选择定时器3的通道2作为PWM的输出引脚TIM3_CH2->PB5 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化引脚

2、初始化定时器:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义初始化结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //自动重装载寄存器的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //TIMX预分频的值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据以上功能对定时器进行初始化

3、设置TIM3_CH2的PWM模式,使能TIM3的CH2输出:

TIM_OCInitTypeDef TIM_OCInitStructure;//定义结构体
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//选择定时器模式,TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//输出比较极性低
TIM_OC2Init(TIM3, &TIM_OCInitStructure);//根据结构体信息进行初始化
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能定时器TIM2在CCR2上的预装载值

4、使能定时器3:

TIM_Cmd(TIM3, ENABLE); //使能定时器TIM3

经过以上的操作,定时器3的第二通道已经可以正常工作并输出PWM波了,只是其占空比和频率都是固定的,我们可以通过改变TIM3_CCR2,则可以控制它的占空比。修改占空比的函数为:TIM_SetCompare2(TIM3,n); n不同,占空比不同。

5、修改pwm波形的占空比:

编写一个函数:void TIM3_PWM_Init(u16 arr,u16 psc);将以上所有的代码都加进来这个函数中,只要在main函数中调用该函数进行初始化,然后使用TIM_SetCompare2()函数修改PWM的占空比就可以在PB5脚得到需要的PWM波形了。关于频率以及占空比的计算方法有以下例子:

int main(void)
{
  TIM3_PWM_Init(9999,143);//频率为:72*10^6/(9999+1)/(143+1)=50Hz
  TIM_SetCompare2(TIM3,4999);//得到占空比为50%的pwm波形
  while(1);
}

可参考:http://www.cnblogs.com/wangh0802PositiveANDupward/archive/2012/12/29/283...

转自:粥巴坨

围观 566

在工作过程中,遇到这样一个产品,它基于 Cortex-M7 内核的 STM32F769 芯片,同时使用了 FreeRTOS 实时操作系统。

由于该产品使用电池供电,因此有着低功耗的需求。

接下来,我将简单描述一下 STM32 与 FreeRTOS 各自的低功耗特性,以及在配合使用时如何去实现产品的低功耗。

一、STM32F769 芯片的三种低功耗模式

STM32F769 支持三种低功耗模式,它们分别是:SLEEP、STOP和STANDBY,其省电能力依次增强。

• SLEEP

在 SLEEP 模式下,只有 Cortex-M7 内核停止了工作,而外设仍然在运行。

在进入 SLEEP 模式后,所有中断均可唤醒 MCU,从而退出 SLEEP 模式。

• STOP

在 STOP 模式下,内核停止工作,并且所有的时钟(如 HCLK, PCLK1, PCLK2 等)也停止工作,即所有外设停止工作,这里有一点要特别注意,此时 SYSTICK 也会被停掉。当然,我们产品中的 RTC 还在继续运行,因为它的时钟源为外部的 32.768K 晶振。

在进入 STOP 模式后,只有外部中断(EXTI)才能唤醒 MCU(由于 RTC 中断挂在外部中断线上,所以 RTC 中断也能唤醒 MCU)。

• STANDBY

在 STANDBY 模式下,内核、所有的时钟、以及后备 1.2V 电源全部停止工作。

从 STANDBY 模式中唤醒后,系统相当于执行了一次复位操作,程序会从头来过。

综上所述,很明显地,在STM32 提供的这三种低功耗模式中,我们只能使用其中的 SLEEP 和 STOP 这两种,STANDBY 不适用。

关于 STM32769 更详细的低功耗内容介绍,请查看 Reference Manual 的4.3节 – Low-power modes.

二、FreeRTOS 的低功耗实现

在启动任务调度器时,FreeRTOS 会创建一个 IDLE 任务,其任务优先级最低,当且仅当所有其它任务均被阻塞时,IDLE 任务才会获得 CPU 使用权。

因此,可以很容易想到在 IDLE 任务里去实现进入与退出 STM32F769 的低功耗模式,即在切入 IDLE 任务后,让 STM32 也进入低功耗模式,而在即将切换出 IDLE 任务之前,去唤醒 STM32。

另外,较新版本的 FreeRTOS 中,增加了 Tickless mode,更详细的介绍请查看参考文献[2].

三、整个产品的低功耗实现

那么在 IDLE 任务里,要如何去确定当前 STM32 应该是进入 SLEEP 还是 STOP 模式呢?

考虑到 SLEEP 和 STOP 两者之间的差异,即 SLEEP 下任何中断均可唤醒 STM32,而在 STOP 下,只能通过外部中断去唤醒,所以,我们的产品采用了如下的机制:

在可确定的将来的一段时间内,如果程序员知道这期间会发生一个非外部中断,这时,就不能让 STM32 进入 STOP 模式。因为,一旦进入了 STOP,STM32 就只能响应外部中断,而不能对非外部中断(如串口、I2C 等外设中断)作出响应。

举个例子会更便于理解。假设这样一个场景 —— 通过中断去读取 I2C 数据。在程序员配置好 I2C 读取数据中断后,系统就处于等待 I2C 中断的状态。之后如果产生了 I2C 中断,就代表数据已经读取完毕,程序员接下来就可以去处理数据了。

接上面的,在配置好 I2C 读取数据中断后,如果此时 IDLE 任务得到执行,那么,这种情况下就不能让 STM32 进入 STOP 模式,而只能进入 SLEEP 模式。一旦产生了 I2C 中断,则 STM32 就会从 SLEEP 中被唤醒。而如果之前 STM32 进入了 STOP 模式,那么这个 I2C 中断就会被略掉了。

所以,在这个产品中,我们提供了两个接口,disable_enter_stop_mode 和 enable_enter_stop_mode,分别用来告知,当前不能进入 STOP 模式和当前可以进入 STOP 模式了。

整理一下,我们可以得到如下的流程图:

STM32与FreeRTOS实现低功耗

如果当前可以进入 STOP 模式,在真正进入 STOP 之前,还有一件事要做——配置 RTC 唤醒定时器,让其在某一时刻来唤醒 STM32。具体能在 STOP 模式下睡多长时间,由 FreeRTOS 中的 prvGetExpectedIdleTime 接口计算得出。

RTC 唤醒定时器配置完成后,即可通过调用 HAL_PWR_EnterSTOPMode 让 STM32 进入 STOP 模式了。如果此时没有任何中断处于 PENDING 状态,则 STM32 会立即进入 STOP 模式,如果此时有中断处于 PENDING 状态,则 STM32 不会进入 STOP 模式,代码会继续往下执行。

在 STM32 处于 STOP 模式期间,如果产生了任何外部中断(EXTI 中断),则 STM32 会被立马唤醒,不管 RTC 唤醒定时器有没有超时。如果期间一直没有外部中断,那么 STM32 会一直处于 STOP 模式,直到 RTC 唤醒定时器超时,从而将 STM32 唤醒。

另外,由于在 STOP 下,为 FreeRTOS 提供心跳时钟的 SYSTICK 也停止了工作,所以,在被唤醒之后,还需要将在 STOP 下流逝的时间告诉 FreeRTOS。

总之,降低整个产品功耗的基本思想,就是让 FreeRTOS 仅可能多的时间处于 IDLE 任务,让 STM32 尽可能多的时间处于 STOP 模式,最终达到尽可能多的降低功耗的目的。

转自: icuic

围观 833

一、三种BOOT模式介绍

所谓启动,一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式。

STM32 BOOT模式配置以及作用

Main Flash memory
是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。

System memory
从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少。系统存储器是芯片内部一块特定的区域,STM32在出厂时,由ST在这个区域内部预置了一段BootLoader, 也就是我们常说的ISP程序, 这是一块ROM,出厂后无法修改。一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的BootLoader中,提供了串口下载程序的固件,可以通过这个BootLoader将程序下载到系统的Flash中。但是这个下载方式需要以下步骤:
Step1:将BOOT0设置为1,BOOT1设置为0,然后按下复位键,这样才能从系统存储器启动BootLoader

Step2:最后在BootLoader的帮助下,通过串口下载程序到Flash中

Step3:程序下载完成后,又有需要将BOOT0设置为GND,手动复位,这样,STM32才可以从Flash中启动可以看到, 利用串口下载程序还是比较的麻烦, 需要跳帽跳来跳去的,非常的不注重用户体验。

Embedded Memory
内置SRAM,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。假如我只修改了代码中一个小小的地方,然后就需要重新擦除整个Flash,比较的费时,可以考虑从这个模式启动代码(也就是STM32的内存中),用于快速的程序调试,等程序调试完成后,在将程序下载到SRAM中。

二、开发BOOT模式选择。

1、通常使用程序代码存储在主闪存存储器,配置方式:BOOT0=0,BOOT1=X;

2、Flash锁死解决办法:

开发调试过程中,由于某种原因导致内部Flash锁死,无法连接SWD以及Jtag调试,无法读到设备,可以通过修改BOOT模式重新刷写代码。

修改为BOOT0=1,BOOT1=0即可从系统存储器启动,ST出厂时自带Bootloader程序,SWD以及JTAG调试接口都是专用的。重新烧写程序后,可将BOOT模式重新更换到BOOT0=0,BOOT1=X即可正常使用。

转自:博乐Bar

围观 1766

1. STM32的Timer简介

STM32中一共有11个定时器,其中2个高级控制定时器,4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。其中系统嘀嗒定时器 ,看门狗定时器暂不讨论。今天主要是研究剩下的8个定时器。

STM32通用定时器功能和用法

其中TIM1和TIM8是能够产生3对PWM互补输出的高级登时其,常用于三相电机的驱动,时钟由APB2的输出产生。TIM2-TIM5是普通定时器,TIM6和TIM7是基本定时器,其时钟由APB1输出产生。由于STM32的TIMER功能太复杂了,所以只能一点一点的学习。因此今天就从最简单的开始学习起,也就是TIM2-TIM5普通定时器的定时功能。

2.普通定时器TIM2-TIM5

2.1 时钟来源

计数器时钟可以由下列时钟源提供:

·内部时钟(CK_INT)

·外部时钟模式1:外部输入脚(TIx)

·外部时钟模式2:外部触发输入(ETR)

·内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

由于今天的学习是最基本的定时功能,所以采用内部时钟。TIM2-TIM5的时钟不是直接来自于APB1,而是来自于输入为APB1的一个倍频器。这个倍频器的作用是:当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其他数值时(即预分频系数为2、4、8或16),这个倍频器起作用,定时器的时钟频率等于APB1的频率的2倍。

通过倍频器给定时器时钟的好处是:APB1不但要给TIM2-TIM5提供时钟,还要为其他的外设提供时钟;设置这个倍频器可以保证在其他外设使用较低时钟频率时,TIM2-TIM5仍然可以得到较高的时钟频率。

2.2 计数器模式

TIM2-TIM5可以由向上计数、向下计数、向上向下双向计数。向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器内容),然后重新从0开始计数并且产生一个计数器溢出事件。

在向下模式中,计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。而中央对齐模式(向上/向下计数)是计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

2.3 编程步骤

1. 配置系统时钟;

2. 配置NVIC;

3. 配置GPIO;

4. 配置TIMER;

第4项配置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。

步骤(4)中的时钟分割定义的是在定时器时钟频率(CK_INT)与数字滤波器(ETR,TIx)使用的采样频率之间的分频比例。TIM_ClockDivision的参数如下表:

STM32通用定时器功能和用法

数字滤波器(ETR,TIx)是为了将ETR进来的分频后的信号滤波,保证通过信号频率不超过某个限定。 步骤(7)中需要禁止使用预装载缓冲器。当预装载缓冲器被禁止时,写入自动装入的值(TIMx_ARR)的数值会直接传送到对应的影子寄存器;如果使能预加载寄存器,则写入ARR的数值会在更新事件时,才会从预加载寄存器传送到对应的影子寄存器。

ARM中,有的逻辑寄存器在物理上对应2个寄存器,一个是程序员可以写入或读出的寄存器,称为preload register(预装载寄存器),另一个是程序员看不见的、但在操作中真正起作用的寄存器,称为shadow register(影子寄存器);设计preload register和shadow register的好处是,所有真正需要起作用的寄存器(shadow register)可以在同一个时间(发生更新事件时)被更新为所对应的preload register的内容,这样可以保证多个通道的操作能够准确地同步。如果没有shadow register,或者preload register和shadow register是直通的,即软件更新preload register时,同时更新了shadow register,因为软件不可能在一个相同的时刻同时更新多个寄存器,结果造成多个通道的时序不能同步,如果再加上其它因素(例如中断),多个通道的时序关系有可能是不可预知的。

3. 程序源代码

本例实现的是通过TIM2的定时功能,使得LED灯按照1s的时间间隔来闪烁

#include "stm32f10x_lib.h"
 
void RCC_cfg();
void TIMER_cfg();
void NVIC_cfg();
void GPIO_cfg();
 
int main()
{
       RCC_cfg();
       NVIC_cfg();
       GPIO_cfg();
       TIMER_cfg();
 
       //开启定时器2
       TIM_Cmd(TIM2,ENABLE);
 
       while(1);
}
 
void RCC_cfg()
{
      
       //定义错误状态变量
       ErrorStatus HSEStartUpStatus;
      
       //将RCC寄存器重新设置为默认值
       RCC_DeInit();
 
       //打开外部高速时钟晶振
       RCC_HSEConfig(RCC_HSE_ON);
 
       //等待外部高速时钟晶振工作
       HSEStartUpStatus = RCC_WaitForHSEStartUp();
       if(HSEStartUpStatus == SUCCESS)
       {
              //设置AHB时钟(HCLK)为系统时钟
              RCC_HCLKConfig(RCC_SYSCLK_Div1);
 
              //设置高速AHB时钟(APB2)为HCLK时钟
              RCC_PCLK2Config(RCC_HCLK_Div1);
 
              //设置低速AHB时钟(APB1)为HCLK的2分频
              RCC_PCLK1Config(RCC_HCLK_Div2);
             
              //设置FLASH代码延时
              FLASH_SetLatency(FLASH_Latency_2);
 
              //使能预取指缓存
              FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
 
              //设置PLL时钟,为HSE的9倍频 8MHz * 9 = 72MHz
              RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
 
              //使能PLL
              RCC_PLLCmd(ENABLE);
 
              //等待PLL准备就绪
              while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
 
              //设置PLL为系统时钟源
              RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
 
              //判断PLL是否是系统时钟
              while(RCC_GetSYSCLKSource() != 0x08);
       }
 
       //允许TIM2的时钟
       RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
       //允许GPIO的时钟
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
 
}
 
void TIMER_cfg()
{
       TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
 
       //重新将Timer设置为缺省值
       TIM_DeInit(TIM2);
       //采用内部时钟给TIM2提供时钟源
       TIM_InternalClockConfig(TIM2);
       //预分频系数为36000-1,这样计数器时钟为72MHz/36000 = 2kHz
       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);
}
 
void NVIC_cfg()
{
       NVIC_InitTypeDef NVIC_InitStructure;
        //选择中断分组1
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
        
        
        //选择TIM2的中断通道
        NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;      
        //抢占式中断优先级设置为0
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
       //响应式中断优先级设置为0
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        //使能中断
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
}
 
void GPIO_cfg()
{
       GPIO_InitTypeDef GPIO_InitStructure;
 
      
       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                 //选择引脚5
       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出频率最大50MHz
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //带上拉电阻输出
       GPIO_Init(GPIOB,&GPIO_InitStructure);
}
在stm32f10x_it.c中,我们找到函数TIM2_IRQHandler(),并向其中添加代码
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(GPIOB,GPIO_Pin_5);
             
              if(ReadValue == 0)
              {
                     GPIO_SetBits(GPIOB,GPIO_Pin_5);
              }    
              else
              {
                     GPIO_ResetBits(GPIOB,GPIO_Pin_5);      
              }
       }
 
}

本文转载自: 博客园 - 沉舟侧畔
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱 demi@eetrend.com 进行处理。

围观 524

通过使最新的STM32 PMSM FOC软件开发套件(SDK)支持STM32Cube开发生态系统(订货代码: X-CUBE-MCSDK),意法半导体进一步简化在STM32* 微控制器上开发先进的高能效电机驱动器的难度。此举为空调、家电、无人机、楼宇自动化、机床、医疗设备、电动车等产品设备工程师研发先进电机驱动带来更多机会,而且无需专门的研发经验。

基于意法半导体上一代永磁同步电机(PMSM)矢量控制(FOC)SDK,5.0 新版固件库结合STM32Cube硬件抽象层(HAL)和底层(LL)架构,简化电机驱动电路的开发、定制和调试过程。此外,免费使用源代码让开发人员能够按照市场需求灵活地设计应用方案,加强电机的控制和定制功能。

作为MC-Workbench 5.0的新功能,图形用户界面(GUI)可以利用STM32CubeMX工作流程创建项目,配置微控制器外设,自动生成初始化代码,还能让用户在项目开发调试过程中实时监视并修改控制回路参数。

新套件包含实现受市场欢迎的PMSM控制技术所需的各种算法,例如,最大限度提升能效和处理负载条件不断变化的最大转矩电流比 (MTPA),以及用于扩大速度范围的弱磁控制和用于强化高转速稳定性的前馈控制。其它功能包括当转子已经在旋转时确保驱动平稳插入的 “运转中启动”功能,室外空调机的风扇或排风扇通常离不开这项功能。

用户可以利用经过市场检验的强大的SDK功能,例如,Motor Profiler有助于快速评测电机的大多数性能,自动检测电气参数(定子电阻(Rs)、电感(Ls)和电机电压常数(Ke))以及机械摩擦和惯性特性。新套件支持各种灵活的电机控制策略,包括基于单路或三路分流电阻的电流检测或隔离型电流检测(ICS)方法,使用编码器或霍尔传感器的转子位置检测或无传感器控制技术。利用很多STM32产品的丰富模拟功能和多个电机控制定时器,新SDK还支持双电机控制应用。

最新的STM32 PMSM FOC SDK免费下载网站: www.st.com/x-cube-mcsdk .

围观 410

一、在IAR EWARM中建立工程的步骤:

1. 建立工程项目文件

新建一个文件夹来存放整个工程项目,在该项目文件夹下建立几个子文件夹存放不同类别的文件:
i. 将官方模板中的stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h和空白main.c文件复制到该项目文件夹下;

ii. Obj-存放工程文件,将官方模板中的cortexm3_macro.s、lnkarm_flash.xcl、lnkarm_ram.xcl和stm32f10x_vector.c文件复制到该文件夹下。

iii. library-存放STM32 FWLib文件,将官方提供的固件库library复制到该文件夹下。

2. 在IAR中建立工程

打开IAR,在Project菜单下新建工程,把该工程存放在刚刚建立的Obj子文件夹下;

3. 工程管理

i. 为了方便项目的管理,在刚建立的项目中添加几个Group用来放置不同类型的文件:

1).FWLib-用来存放所需固件库的头文件;
2).StartUp-用来存放STM32的启动代码,添加cortexm3_macro.s和stm32f10x_vector.c(中断向量表);
3).User-用来存放用户文件,添加main.c和stm32f10x_it.c(中断空函数)

ii. 进行项目设置:

在工程上单击右键,选择Option,打开工程设置窗口:
1).在General Options->Target->Device中选择ARM器件型号“ST STM32F10x”;
2).在C/C++ Compiler->Preprocessor->Additional include directories中填入
$PROJ_DIR$\..\
$PROJ_DIR$\..\library\inc

注:$PROJ_DIR$表示工程所在路径,\..\表示返回上一级目录。

3).在linker->config->Linker command file里选中Override default,然后根据实际情况填入$PROJ_DIR$\lnkarm_ram.xcl,并同时在C/C++ Compiler->Preprocessor->Defined Symbols里填入“VECT_TAB_RAM”(在RAM中调试);
或者
$PROJ_DIR$\lnkarm_flash.xcl,并同时在C/C++ Compiler->Preprocessor->Defined Symbols里填入“VECT_TAB_FLASH”(在FLash中调试);

4).在Debugger->Driver中选择“Third-Party Driver”,在Third-Party Driver->IAR debugger Driver中填入ST LINKII的驱动C:\Manley\drivers\STLink\STM32Driver.dll”;

4. 编译调试

在stm32f10x_conf.h中将没有用到的外设注销,将所需外设固件库的头文件添加到工程中;

转自: QFLSD

围观 605

存储器映射是指把芯片中或芯片外的FLASH,RAM,外设,BOOT,BLOCK等进行统一编址。即用地址来表示对象。
这个地址绝大多数是由厂家规定好的,用户只能用而不能改。用户只能在挂外部RAM或FLASH的情况下可进行自定义。

Cortex-M3支持4GB的存储空间,它的存储系统采用统一编址的方式; 程序存储器、数据存储器、寄存器被组织在4GB的线性地址空间内,以小端格式(little-endian)存放。由于Cortex-M3是32位的内核,因此其PC指针可以指向2^32=4G的地址空间,也就是0x0000_0000——0xFFFF_FFFF这一大块空间。见图1:

STM32的存储器映射详解
图1 Cortex-M3的存储器映射

Cortex-M3内核将0x0000_0000——0xFFFF_FFFF这块4G大小的空间分成8大块:代码、SRAM、外设、外部RAM、外部设备、专用外设总线-内部、专用外设总线-外部、特定厂商(见图1)。这就导致了,使用该内核的芯片厂家必须按照这个进行各自芯片的存储器结构设计,如stm32。

STM32的存储器映射详解
图2 Cortex-M3与中密度stm32的存储器映射对比

图2中可以很清晰的看到,STM32的存储器结构和Cortex-M3的很相似(这是因为stm32本来就是按照cortex_m3内核来设计硬件的),不同的是,STM32加入了很多实际的东西,如:Flash、SRAM等。只有加入了这些东西,才能成为一个拥有实际意义的、可以工作的处理芯片——STM32。STM32的存储器地址空间被划分为大小相等的8块区域,每块区域大小为512MB(如:0x20000000~0x40000000)。对STM32存储器知识的掌握,实际上就是对Flash和SRAM这两个区域知识的掌握。

不同类型的STM32单片机的SRAM大小是不一样的,但是他们的起始地址都是0x2000 0000,终止地址都是0x2000 0000+其固定的容量大小。SRAM的理解比较简单,其作用是用来存取各种动态的输入输出数据、中间计算结果以及与外部存储器交换的数据和暂存数据。设备断电后,SRAM中存储的数据就会丢失。

STM32的Flash,严格说,应该是Flash模块。三个分区的称呼与datasheet保持一致。该Flash模块包括:

Flash主存储区(Main memory)Flash:存放代码的地方,如图2中的FLASH区域:128KB(0x08000000~0x0801ffff)(不同容量的Flash终止地址不同);

Flash信息区(Information block),该区域又可以分为Option Bytes和System Memory区域;System Memory:STM32在出厂时,已经固化了一段程序在System memory(medium-density devices的地址为:0x1FFF_F000,大小为2KB)存储器中。这段程序就是一个固定好的,并且没法修改的Boot Loader(见编程手册PM0042这种描述)。Option Bytes:可以按照用户的需要进行配置(如配置看门狗为硬件实现还是软件实现);该区域除了互联型所用型号地址都一样:(0x1fff_f000~0x1fff_f80f)图中终止地址有误:应为0x1fff_f80f,正好16个字节。

Flash存储接口寄存器区(Flash memory interface),用于片上外设。是图2中从0x40000000开始的PERIPHERALS区域。也称作外设存储器映射,对该区域操作,就是对相应的外设进行操作。

根据STM32的内存映射图,在代码区,0x00000000地址为启动区,上电以后,CPU从这个地址开始执行代码。0x08000000是用户FLASH的起始地址,0x20000000是SRAM的起始地址。

STM32的存储器映射详解

来源: rh0932

围观 904

串口设置的一般步骤可以总结为如下几个步骤:

1) 串口时钟使能, GPIO 时钟使能
2) 串口复位
3) GPIO 端口模式设置
4) 串口参数初始化
5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6) 使能串口
7) 编写中断处理函数

1.串口时钟使能。

串口是挂载在 APB2 下面的外设,所以使能函数为:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);

2.串口复位。

void USART_DeInit(USART_TypeDef* USARTx);//串口复位

3.串口参数初始化。

串口初始化是通过 USART_Init()函数实现的,

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

USART_InitStructure.USART_BaudRate = bound; //波特率设置;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位数据格式

USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位

USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位

USART_InitStructure.USART_HardwareFlowControl
= USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口

4.数据发送与接收。

发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR

发送:void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

接收:uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

STM32串口配置步骤

RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。

TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式: 1)读 USART_SR,写USART_DR。 2)直接向该位写 0。

在我们固件库函数里面,读取串口状态的函数是:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

例如我们要判断读寄存器是否非空(RXNE), 操作库函数的方法是:USART_GetFlagStatus(USART1, USART_FLAG_RXNE);我们要判断发送是否完成(TC),操作库函数的方法是:USART_GetFlagStatus(USART1, USART_FLAG_TC);

6.串口使能。

串口使能是通过函数 USART_Cmd()来实现的,这个很容易理解,使用方法是:
USART_Cmd(USART1, ENABLE); //使能串口

7.开启串口响应中断。

有些时候当我们还需要开启串口中断,那么我们还需要使能串口中断,使能串口中断的函数是:
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)

比如在接收到数据的时候(RXNE 读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是:USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断我们在发送数据结束的时候(TC, 发送完成) 要产生中断,那么方法是:USART_ITConfig(USART1, USART_IT_TC, ENABLE);

8.获取相应中断状态。

经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是:USART_GetITStatus(USART1, USART_IT_TC)

转自: zengsf

围观 419

页面

订阅 RSS - STM32