第三周学习内容

第三周学习内容

stm32时钟系统

硬件

  1. STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。
      ①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
       ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时
    钟源,频率范围为4MHz~16MHz。
       ③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。WDG
       ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC
       ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。
    倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
  2. 系统时钟SYSCLK可来源于三个时钟源:
    ①、HSI振荡器时钟
    ②、HSE振荡器时钟
    ③、PLL时钟
    3.STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL
    输出的2分频、HSI、HSE、或者系统时钟。
    4.任何一个外设在使用之前,必须首先使能其相应的时钟。

函数设定举例

如果定义一个频率就去执行相应的函数

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#endif

若设置为72mhz就调用SetSysClockTo72();
这个函数为

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* Enable HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);//启用HSE时钟
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
判断时钟是否启动完成

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)启动完成执行以下函数
  {
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

 
    /* HCLK = SYSCLK */72mhz
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */72mhz
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK /2*/ 36mhz
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
这三行设置时钟的分频系数

详细配置参数参考stm32参考手册6.3.2时钟配置寄存器rcc_cfgr

在这里插入图片描述

Delay函数

最大延迟 ms 数可以通过公式:nms<=0xffffff81000/SYSCLK 计算。SYSCLK 单位为 Hz,nms 的单位为 ms。如果时钟为 72M,那么 nms 的最大值为 1864ms。超过这个值,建议通过多次调用delay_ms 实现,否则就会导致延时不准确。

Systick定时器

Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。

SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。
Systick中断的优先级也可以设置。

UCOSII 的时钟

ucos 运行需要一个系统时钟节拍(类似 “心跳”),而这个节拍是固定(OS_TICKS_PER_SEC 宏定义设置),比如要求 5ms 一次(即可设置:OS_TICKS_PER_SEC=200),在 STM32 上面,一般是由 SysTick 来提供这个节拍,也就是 SysTick要设置为 5ms 中断一次,为 ucos 提供时钟节拍,而且这个时钟一般是不能被打断的(否则就不准了)。

#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII
OS_ERR err; 
OSSchedUnlock(&err); //UCOSIII 的方式,恢复调度
#else //否则 UCOSII
OSSchedUnlock(); //UCOSII 的方式,恢复调度
#endif
}
//调用 OS 自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII 时
OS_ERR err; 
OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);//UCOSIII 延时采用周期模式
#else
OSTimeDly(ticks); //UCOSII 延时
#endif 
} 
//systick 中断服务函数,使用 ucos 时用到
void SysTick_Handler(void)
{
if(delay_osrunning==1) //OS 开始跑了,才执行正常的调度处理
{
OSIntEnter(); //进入中断
OSTimeTick(); //调用 ucos 的时钟服务程序 
OSIntExit(); //触发任务切换软中断
} }
以上代码,仅支持 UCOSII 和 UCOSIII,不过,对于其他 OS 的
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8)

这一句把SysTick的时钟选择外部时钟,这里需要注意的是:SysTick 的时钟源自 HCLK 的 8 分频,假设我们外部晶振为 8M,然后倍频到 72M,那么 SysTick 的时钟即为 9Mhz,也就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/9us。所以fac_us=SystemCoreClock/8000000;这句话就是计算在 SystemCoreClock时钟频率下延时 1us 需要多少个 SysTick 时钟周期。同理,fac_ms=(u16)fac_us*1000;就是计算延时 1ms 需要多少个 SysTick 时钟周期,它自然是 1us 的 1000 倍。初始化将计算出 fac_us 和fac_ms 的值。

fac_us为 8 位整形数据,fac_ms 为 16 位整形数据。

在不使用 OS 的时候:fac_us,为 us 延时的基数,也就是延时 1us,SysTick->LOAD 所应设置的值。fac_ms 为 ms 延时的基数,也就是延时 1ms,SysTick->LOAD 所应设置的值。fac_us为 8 位整形数据,fac_ms 为 16 位整形数据。Systick 的时钟来自系统时钟 8 分频,正因为如此,系统时钟如果不是 8 的倍数(不能被 8 整除),则会导致延时函数不准确,这也是我们推荐外部时钟选择 8M 的原因。这点大家要特别留意。当使用 OS 的时候,fac_us,还是 us 延时的基数,不过这个值不会被写到 SysTick->LOAD寄存器来实现延时,而是通过时钟摘取的办法实现的(前面已经介绍了)。而 fac_ms 则代表 ucos自带的延时函数所能实现的最小延时时间(如 delay_ostickspersec=200,那么 fac_ms 就是 5ms)。

时钟摘取法

当采用OS时,SysTick定时器(24位,向下计数)作为OS的系统时钟,SysTick定时器不能被修改,如果想用SysTick定时器实现精确延时只能读取SysTick定时器中的计数值:

例如:delayUs(100) 延时100us;

1.在进入delayUs函数时计算达到100us延时需要的SysTick计数值是多少;比如:当前SysTick计数值为2000,100us延时后SysTick的计数值为 2000+100108=12800; 如SysTick计数时钟为108MHz,1个时钟的时间为1/108000000s,即1/108000ms,1/108us,1us需要的计数次数为108,1ms需要的计数次数为108000;100us=108100次;

2.在delayUs函数中循环读取SysTick定时器的计数值,当计数值大于12800后,延时时间到退出循环;

注意:此法不适合长时间的延时;当超SysTick定时器的最大计数值时要考滤计数器溢出前计数值,和溢出后计数值;

delay_ms函数

//延时 nms
//注意 nms 的范围
//SysTick->LOAD 为 24 位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK 单位为 Hz,nms 单位为 ms
//对 72M 条件下,nms<=1864 
void delay_ms(u16 nms)
{ 
u32 temp; 
SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD 为 24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达 
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器 
}

延时的 ms 数不能太长。否则超出了 LOAD 的范围,高位会被舍去,导致延时不准。最大延迟 ms 数可以通过公式:nms<=0xffffff81000/SYSCLK 计算。SYSCLK 单位为 Hz,nms 的单位为 ms。如果时钟为 72M,那么 nms 的最大值为 1864ms。超过这个值,建议通过多次调用 delay_ms 实现,否则就会导致延时不准确。

端口复用

  1. 端口复用,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做端口复用。STM32 有很多的内置外设,这些部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。比如,大家都知道, MCU 都有串口,STM32 有好几个串口。比如说STM32F103ZET6 有 5个串口,我们可以查手册知道,串口1的 引脚对应IO 为 PA9,PA10. PA9 ,PA10 默认功能是 GPIO,所以当 PA9,PA10 引脚作为串口 引脚作为串口 1的 TX,RX 引脚使用的时候,那就是端口复用。

  2. 这里用PA9和PA10两个端口进行举例
    复用端口初始化有几个步骤:
    1)GPIO 端口时钟使能。要使用到端口复用,当然要使能端口的时钟了。
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE)
    2)复用的外设时钟使能。比如你要将端口 PA9,PA10 复用为串口,所以要使能串口时钟。
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    3)端口模式配置。在IO 复用位内置外设功能引脚的时候,必须设置GPIO端口的模式,至于 在复用功能下 GPIO 的模式是怎么对应的
    在这里插入图片描述
    要配置全双工的串口 1,那么TX管脚需要配置为推挽复用输出,RX 管脚配置为浮空输入或者带上拉输入。
    //USART1_TX PA.9 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 
GPIO_Init(GPIOA, &GPIO_InitStructure); 
//USART1_RX

PA.10 浮空输入

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 
GPIO_Init(GPIOA, &GPIO_InitStructure); 

STM32 NVIC中断管理

CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32 有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。而我们常用的就是这 68 个可屏蔽中断,但是 STM32 的 68 个可屏蔽中断,在 STM32F103系列上面,又只有 60 个(在 107 系列才有 68 个)。因为我们开发板选择的芯片是 STM32F103 系列的所以我们就只针对 STM32F103 系列这 60 个可屏蔽中断进行介绍。
抢占,
是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由NVIC_IRQChannelPreemptionPriority 的参数配置

响应
属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断,响应属NVIC_IRQChannelSubPriority参数配置。
在这里插入图片描述

typedef struct
{
 __IO uint32_t ISER[8]; /*!< Interrupt Set Enable Register */
 uint32_t RESERVED0[24];
 __IO uint32_t ICER[8]; /*!< Interrupt Clear Enable Register */
 uint32_t RSERVED1[24];
 __IO uint32_t ISPR[8]; /*!< Interrupt Set Pending Register */
 uint32_t RESERVED2[24];
 __IO uint32_t ICPR[8]; /*!< Interrupt Clear Pending Register */
 uint32_t RESERVED3[24];
 __IO uint32_t IABR[8]; /*!< Interrupt Active bit Register */
 uint32_t RESERVED4[56];
 __IO uint8_t IP[240]; /*!< Interrupt Priority Register, 8Bit wide */
 uint32_t RESERVED5[644];
 __O uint32_t STIR; /*!< Software Trigger Interrupt Register */
} NVIC_Type;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值