非常荣幸能获得此次对Nucleo-F410RB的评测机会。
因为本人一直学习寄存器开发,没有用过库函数,更没用过CubeMx,不会写华丽的测评**,所以这次评测将着重技术角度体验这款Nucleo开发板。
先晒一下板子~~
除了芯片,与其他的Nucleo一样。STM32 Nucleo板遵循精益方法,是原型开发及社区使用的理想工具选择。Arduino™连接器和STMorpho连接器方便用户轻松使用应用相关的附加硬件进行扩展。MCU的所有I/O都能方便访问,该板自带HAL库,可工作于IAR、Keil、基于CCC的IDE以及所有mbed™在线IDE。 Nucleo简约明了,没有搭载额外的器件。外部高速晶振没有提供,默认连接了ST-Link的MCO(8MHZ)输出,故不影响用户使用外部时钟,当然也可以自行安装外部晶振。
Nucleo家族:
Nucleo涵盖了STM32下的主要微控制器款型,为开发者熟悉与使用STM32提供了一个很好的台阶。
这次评测,将着重与Nucleo411RE比较,这两款芯片具有相同的主频,与类似的外设,从他俩的差异中看看410的来意何在。
一、 两者的功能框图比较
STM32F410RB
STM32F411RE
可以看出STM32F410RB相对STM32F411RE,对外设做了一定的剪裁,去掉了SDIO控制器和USB 2.0OTG FS控制器,加入了一个DAC和硬件真随机数生成器TRNG。框图,,,还是很片面的,让我惊讶的是411里面竟然也有BAM,这个我之前在411的参考手册是没有看到的。总之,通过框图,发现这两个芯片的差别很小。 再看一下两者的参考手册吧那就,,,
二、低功耗模式对比 STM32F410RB
STM32F411RE
可以看到,410比411多了一个BAM模式,当然我并不清楚ST是官方是框图搞错了还是手册漏写了,我暂时没有过试验。
三、外设比对 STM32F410RB
STM32F411RE
在AHB1总线上,410比411多了一个真随机数发生器,评测时可以试用一下。411还有一个AHB2总线,因为410是带USBOTG的,410没有。
STM32F410RB
STM32F411RE 在APB1总线上,410RB相比于411RE多了一个DAC和一个低功耗定时器(LPTIM1),至于那个RTCAPBEN,是410为RTC新添加的一个时钟使能开关,之前是没有的,所以在对410的编程中需要额外注意。410RB比411RE少了三个通用定时器TIM2、TIM3、TIM4。
STM32F410RB
STM32F411RE
在APB2总线上410减少了TIM10、SPI4、SD卡控制器这三个外设。 从410与411的对比可以看出,410更侧重于传感器和数据采集方向,更好的与当下的十分火热物联网接轨。填补了F4系列里的一个空白区,BAM的引入进一步促进了低功耗。
四、基本外设试用 使用前需要注意一件事情,可能是ST的疏忽,ST-link固件版本有点跟不上时代,导致了虚拟盘符故障,情况如下: 这里原本应该是128KB的可用空间,但现在没有空间可用。去官网下载最新的固件,更新ST-Link固件。
1、随机数发生器(RNG) RNG框图 随机数生成器的基本配置过程为: 1、 随机数时钟(RNG_CLK)已经在系统初始化函数中设置了,总线2分频。 2、 然后使能RNG时钟 其次是随机数控制寄存器(RNG_CR) 一共只有两个控制位,中断允许位IE,RNG使能位RNGEN。 介绍三个重要的状态位: SEIS:种子错误中断状态,置位后会触发中断,写0清除此位 CEIS:时钟错误中断状态,置位后会触发中断,写0清除此位 DRDY:数据就绪状态位,置位后会触发中断,该位为只读位,当程序读DR寄存器时自动清零。 RNG代码:
#include "sys_f4.h"
#include "TRNG.h"
u8 RDNUM_Flag=0;
void TRNG_init(void)
{
RCC->AHB1ENR|=0x80000000; //使能RNG时钟
RNG->CR=0x08; //开启RNG中断
NVIC->ISER[2]|=(u32)0x01<<(80-64); //内核中断设置
NVIC->IP[80]=0xf0; //设置RNG中断为最低优先级
RNG->CR|=0x04; //使能RNG
}
u32 GET_Random_NUM(void)
{
u32 Random_k;
if(RDNUM_Flag==GET_RDNUM)
{
RDNUM_Flag=0;
Random_k=RNG->DR;
RNG->CR=0x0C;
return Random_k;
}
else
{
RNG->CR=0x0C;
return 0xFFFFFFFFUL;
}
}
s32 GET_Random_int_NUM(s32 min,s32 max)
{
s32 Random_k;
u32 Random_uk,modtp;
if(RDNUM_Flag==GET_RDNUM)
{
RDNUM_Flag=0;
Random_uk=(u32)RNG->DR;
modtp=max-min+1;
Random_k=Random_uk%modtp+min;
RNG->CR=0x0C;
}
else
{
RNG->CR=0x0C;
return 0x7FFFFFFFL;
}
return (s32)Random_k;
}
void RNG_IRQHandler(void)
{
if(RNG->SR&0x01)
{
RNG->CR=0x00;
RDNUM_Flag=GET_RDNUM;
}
else
{
RNG->SR=0;
RDNUM_Flag=RDNUM_ERROR;
}
}
主函数程序:
程序编译完成后,下载程序。。。。
通过串口像电脑发回数据,按一次按键发回一个数据,拿肉眼看随机性还不错。
2、低功耗定时器(LPTIM1) 低功耗定时器的时钟具有多个输入源,包括APB clock、LSE、LSI、HIS、Input1、Input2,可以编程选择,并且两个外部输入都带有输入滤波器。定时器有一个比较输出。
低功耗定时器主要特性: •16位向上计数器 •3位分频器8种分频因子(1,2,4,8,16,32,64128) •可选择的时钟 –内部时钟源:LSE,LSI,HSI或APB时钟 –外部时钟源输入(工作在没有振荡器运行,脉冲计数器应用程序) •16位自动重装载寄存器 •16位比较寄存器 •连续/单脉冲模式 •可选软件/硬件输入触发器 •可编程数字干扰滤波器 •可配置输出:脉冲、脉宽调制 •可配置的IO极性 •编码器模式 低功耗定时器是一个16位的定时器,增加降低功耗的效益。由于它的时钟源的多样性,定时器能够保持运行在除了待机模式的所有功率模式。即使没有内部时钟源也能继续运行,此时定时器可以作为一个“脉冲计数器”,在某些应用中可能是有用的。同时,该定时器能从低功耗模式系统唤醒,使它能够实现更低的功耗。 该定时器提供了一种灵活的时钟方案,提供所需的功能和性能,同时最大限度地降低功耗。 定时器的功能十分丰富,这里只使用了定时器中断。 低功耗定时器的时钟选择需要特别注意一下,如果使用内部时钟,需要在RCC寄存器选择。
然后来看LPTIM的几个关键寄存器。 ①、中断使能寄存器:IER 和别的定时器的差别有点大,没有了定时器更新中断,取而代之的是自动重载中断。编码器模式新增了两个中断,向上变换中断和向下变换中断,非常的人性化。
②、配置寄存器CFGR
该寄存器包含了定时器所有的模式配置、分频设置、以及时钟源选择。
③、第三个要说一下的是CNT计数寄存器
和其他定时器有所不同的是,低功耗定时器的CNT寄存器是只读寄存器。因为我之前有项目中需要对CNT清零,所以这点我比较关心。
④、选择寄存器LPTIM1_OR 这个寄存器是用于选择input1的输入源的。 可见这个新型定时器在低功耗方面还是下了很多功夫的,就是为低功耗而来。对以后的应用肯定会有很大的帮助,能起到挺大的作用。
最后再试写一个二相步进电机的程序: 主程序: #include "sys_f4.h"
void tim6_init(void)
{
RCC->APB1ENR|=0x10;
TIM6->ARR=49999;
TIM6->PSC=9;
TIM6->DIER=0x01;
NVIC->ISER[1]|=(u32)0x01<<22;
TIM6->CR1=0x01;
}
int main(void)
{
u8 k=0;
SYSTEMHARD();
tim6_init();
BJDJIO_init();
while(1)
{
if(LPTIM1->CNT) GPIOA->BSRRH=0x20;
if(Read_Key()==CLICK)
{
if(k)
{
TIM6->CR1=0x01;
k=0;
}
else
{
TIM6->CR1=0x00;
k=1;
}
}
}
}
void TIM6_DAC_IRQHandler(void)
{
if(TIM6->SR&0x01)
{
Motor1_Drive(1);
}
TIM6->SR=0;
}
电机驱动程序: #include <sys_f4.h>
#define Motor1_SA0 GPIOA->BSRRH=0X10
#define Motor1_SB0 GPIOB->BSRRH=0X01
#define Motor1_SC0 GPIOA->BSRRH=0X02
#define Motor1_SD0 GPIOA->BSRRH=0X01
#define Motor1_SA1 GPIOA->BSRRL=0X10
#define Motor1_SB1 GPIOB->BSRRL=0X01
#define Motor1_SC1 GPIOA->BSRRL=0X02
#define Motor1_SD1 GPIOA->BSRRL=0X01
void BJDJIO_init(void)
{
IOset(GPIOA,0x110155,0x51F,1,3,0,0,1);
IOset(GPIOB,0x41,0x09,1,3,0,0,1);
}
static void Motor1_Step(u8 step)
{
switch (step)
{
case 0: Motor1_SA1; Motor1_SB0; Motor1_SC0; Motor1_SD0; break;
case 1: Motor1_SA0; Motor1_SB0; Motor1_SC1; Motor1_SD0; break;
case 2: Motor1_SA0; Motor1_SB1; Motor1_SC0; Motor1_SD0; break;
case 3: Motor1_SA0; Motor1_SB0; Motor1_SC0; Motor1_SD1; break;
}
}
void Motor1_Drive(u32 FX)
{
static u8 Next=0;
Motor1_Step(Next);
if(FX) Next++;
else Next--;
if(Next==4) Next=0;
if(Next==255) Next=3;
}
谢谢阅读~
|