打印
[ARM入门]

【STM8入门】第11讲 STM8系统时钟控制

[复制链接]
1873|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
刘洋讲单片机|  楼主 | 2016-11-17 15:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 刘洋讲单片机 于 2017-1-11 15:37 编辑

STM8系统时钟控制.pdf (384.16 KB)

众拳【剑齿虎】STM8开发板学习笔记分享
点击此处查看全部资料
第11讲   STM8系统时钟控制

目 录
11.1概述        2
11.2时钟源        3
11.2.1  HSE        4
11.2.2  HSI        5
11.3主时钟切换        6
11.4程序文件设计        7
11.4.1  lib文件中内容分析        7
11.4.2  main.c文件中的程序        8
11.4.3  led.c文件中的程序        10
11.4.4  led.h文件中的程序        11

11.1概述
    时钟控制器功能强大而且灵活易用。其目的在于使用户在获得最好性能的同时,亦能保证消耗的功率最低。
    用户可独立地管理各个时钟源,并将它们分配到CPU或各个外设。主时钟和CPU时钟均带有预分频器。具有安全可靠的无故障时钟切换机制,可在程序运行中将主时钟从一个时钟源切换到另一个时钟源。
抗电磁干扰时钟配置寄存器
    为了避免由电磁干扰造成的对应用程序误写操作或系统挂起,大多数关键的时钟配置寄存器都有一个互补寄存器与之相对应。系统将会自动检测这些关键寄存器与其互补寄存器之间是否匹配。如果不匹配,则产生一个EMS复位,从而使应用程序恢复到正常操作。详情请参见时钟寄存器描述。
11.2时钟源
    下面4种时钟源可用做主时钟:
● 1-24MHz高速外部晶体振荡器(HSE)
● 最大24MHz高速外部时钟信号(HSE user-ext)
● 16MHz高速内部RC振荡器(HSI)
● 128KHz低速内部RC(LSI)
各个时钟源可单独打开或关闭,从而优化功耗。


图11.1  时钟树
11.2.1  HSE
高速外部时钟信号可由下面两个时钟源产生:
● HSE外部晶体/陶瓷谐振器
● HSE用户外部有源时钟
外部晶体/陶瓷谐振器(HSE 晶体)
外部1至24MHz的振荡器其优点在于能够产生精确的占空比为50%的主时钟信号。
硬件连接如图11.2所示。更多详情请参见ST公司数据手册电特性章节。振荡器在启动时的输出时钟信号是不稳定的,默认情况下,在时钟信号被使用之前会插入2048个振荡器周期的延迟。用户可通过设置选项字节HSECNT来缩短稳定时间,请参见ST公司数据手册的选项字节章节。
外部时钟寄存器CLK_ECKR中的标志位HSERDY用以指示高速外部振荡器是否稳定。启动时,
HSE时钟信号将不会生效直至此标志位被硬件置位。
HSE晶体可通过设置外部时钟寄存器CLK_ECKR中的HSEEN位来打开或关闭。
外部时钟源(HSE用户外部时钟)
这种模式下,必须由用户提供一个外部时钟,此时钟的最高频率可为24MHz。用户可通过编程选项位EXTCLK选择此模式。详情请参见数据手册的选项字节章节。此时,占空比约50%的外部时钟信号(方波,正弦波,三角波)用以驱动OSCIN引脚,而OSCOUT引脚可做为通用输入/输出管脚使用。请参见 图11.1。
11.2.2  HSI
HSI信号由内部16MHz RC振荡器与一个可编程分频器(分频因子从1至8)产生。分频因子由寄存器CLK_CKDIVR决定。
注意:启动时,主时钟源默认为 HSI RC 时钟的 8 分频,即 f HSI /8。HSI RC可以提供一个低成本的16MHz时钟源(无需外部器件),其占空比为50%。HSI启动速度比HSE晶体振荡器快,但是其精度即使经过校准也仍然比外部晶体振荡器或陶瓷谐振器低。
内部时钟寄存器CLK_ICKR中的标志位HSIRDY用以指示HSI RC是否稳定。启动时,HSI时钟信号将不会生效直至此标志位被硬件置位。
HSI RC可通过设置内部时钟寄存器CLK_ICKR中的HSIEN位打开或关闭。

图11.2  HSE时钟源
11.3主时钟切换
时钟切换功能为用户提供了一种易用、快速、安全的从一个时钟源切换到另一个时钟源的途径。
为使系统快速启动,复位后时钟控制器自动使用HSI的8分频(HSI/8是2M)做为主时钟。其原因为HSI的稳定时间短,而8分频可保证系统在较差的VDD条件下安全启动。一旦主时钟源稳定,用户程序可将主时钟切换到另外的时钟源。
(1) 配置内部高速振荡器(HSI)的分频器--1分频,C语言语句如下:
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
(2)配置外部高速振荡器(HSE)的分频器--1分频,C语言语句如下:
    CLK_HSECmd(ENABLE);//外部时钟开  
    CLK_LSICmd(ENABLE);//内部低频RC开  
    CLK_HSICmd(ENABLE);//内部高频RC开   
    CLK_ClockSwitchCmd(ENABLE); //切换使能  
  CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO ,
                        CLK_SOURCE_HSE,
                        DISABLE,CLK_CURRENTCLOCKSTATE_DISABLE);//切换配置
11.4程序文件设计
新建立工程文件。我们自定义了main.c、pbdata.c和pbdata.h三个函数文件,全部放置在“USR”文件夹中。pbdata.c和pbdata.h函数文件主要存放我们自定义的公共函数和全局变量,以方便以后每个功能模块之间传递参数。
在这里我们自定义了BSP文件夹,在我们的设计规划中,把所有的底层驱动函数全部放在这个文件中,使程序的可读性很好。下面详细说明此驱动函数书写规范。在这个程序中我们改变分频值,由1分频改为2分频,实验中可以看到LED闪烁速度变慢。
11.4.1  lib文件中内容分析
我们建立一个“Lib”文件夹,把引用系统本身定义好的库函数加在其中,我们使用到哪个外设功能,直接引用到此文件夹中即可。一般情况下下面这三个文件都得引用。
Stm8s_gpio.c   // 定义GPIO函数                           
Stm8s_clk.c    // 定义时钟函数
Stm8s_conf.h   // 外设声明函数(头文件)     

图11.3  stm8s_conf.h头文件内部选择
本节实验及以后的实验我们都是用到库文件,其中stm8s_conf.h头文件包含了所有外设的声明头文件,在本试验中我们使用到时钟外设和GPIO端口外设。所以#include "stm8s_clk.h"和#include "stm8s_gpio.h"需要“打开”(就是把语句前面的双斜线注释符号删除即可,参考图11.4)。Stm8s_gpio.c库函数包含了所有管脚初始化功能和具体的执行操作;Stm8s_clk.c库函数主要包含了内部时钟和外部时钟设置,在主函数中规定了具体的执行方法,在这个函数中就有具体相关的执行步骤。
11.4.2  main.c文件中的程序
主程序就实现初始化和调用驱动程序,这样主程序控制思路清晰,流程简单。在以后的课节中我们就不再描述主程序的书写过程,留出主要思路讲述驱动部分的编写过程。详情请大家参考光盘(网盘)中程序及程序注释。

*   开发平台:剑齿虎STM8开发板
*   作    者: 刘洋 张殿东
*   版    本: V1.0
*   日    期: 2016-05-03   
*
*   IAR开发环境    版本 V2.20.1
*   ST库函数       版本 V2.2.0
***********************************************************************/
#include "pbdata.h"//引入自定义公共头文件
void BSP_Configuration(void);//硬件初始化函数声明
/***********************************************************************
*   函 数 名: main
*   功能说明: c程序入口
*   形    参:无
*   返 回 值: 错误代码(无需处理)
***********************************************************************/
int main(void)
{
  BSP_Configuration();//硬件驱动初始化函数
    while(1)//主程序循环,反复执行循环体里的语句
  {
    //LED_Demo1();//在主程序中调用LED_Demo1()函数
    LED_Demo2();//在主程序中调用LED_Demo2()函数
  }
}
/***********************************************************************
*   函 数 名: BSP_Configuration
*   功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
*   形    参:无
*   返 回 值: 无
***********************************************************************/
void BSP_Configuration(void)
{  
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV2);//时钟速度为内部16M,2分频,
  LED_Init();//调用LED初始化函数
}
/*断言函数:它的作用是在编程的过程中为程序提供参数检查*/
#ifdef USE_FULL_ASSERT
void assert_failed(u8* file,u32 line)
{
  while(1)
  {
  }
}
#endif
11.4.3  led.c文件中的程序
#include "pbdata.h"
/**************************************************************************
*   函 数 名: LED_Init
*   功能说明: LED的GPIO管脚初始化
*   形    参:无
*   返 回 值: 无
**************************************************************************/
void LED_Init(void)
{
  GPIO_Init(LED1_PORT, LED1_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);//初始化LED1管脚为低速开漏输出高阻态
  GPIO_Init(LED2_PORT, LED2_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);//初始化LED2管脚为低速开漏输出高阻态
  GPIO_Init(LED3_PORT, LED3_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);//初始化LED3管脚为低速开漏输出高阻态
}
/**************************************************************************
*   函 数 名: LED_Demo1
*   功能说明: 闪烁例程,间隔时间为1秒。
*   形    参:无
*   返 回 值: 无
***************************************************************************/
void LED_Demo1(void)
{
    LED1_L;//拉低PE0引脚,LED1发光二极管(发光)
    LED2_L;//拉低PA3引脚,LED2发光二极管(发光)
    LED3_L;//拉低PA6引脚,LED3发光二极管(发光)
    delay_ms(1000);//延时1秒
    LED1_H;//拉高PE0引脚,LED1发光二极管(熄灭)
    LED2_H;//拉高PA3引脚,LED2发光二极管(熄灭)
    LED3_H;//拉高PA6引脚,LED3发光二极管(熄灭)
    delay_ms(1000);//延时1秒
}
/**************************************************************************
*   函 数 名: LED_Demo2
*   功能说明: 闪烁例程,间隔时间为1秒。
*   形    参:无
*   返 回 值: 无
**************************************************************************/
void LED_Demo2(void)
{
    LED1_R;//PE0引脚电平取反,高电平时LED1发光二极管(熄灭),底电平时LED1发光二极管(发光)
    LED2_R;//PA3引脚电平取反,高电平时LED2发光二极管(熄灭),底电平时LED2发光二极管(发光)
    LED3_R;//PA6引脚电平取反,高电平时LED3发光二极管(熄灭),底电平时LED3发光二极管(发光)
    delay_ms(1000);//延时1秒
}
11.4.4  led.h文件中的程序
/***********************************************************************
*    剑齿虎STM8开发板硬件连接
*    LED1-PE0         
*    LED2-PA3         
*    LED3-PA6         
************************************************************************/
#ifndef _LED_H  //宏定义,定义文件名称
#define _LED_H
#include "stm8s.h"//引用STM8头文件
/*---------------------------宏定义声明-------------------------------/*
#define LED1_PIN GPIO_PIN_0  //定义GPIO_PIN_0引脚为LED1_PIN,相当于重新命名。
#define LED2_PIN GPIO_PIN_3  //定义GPIO_PIN_3引脚为LED2_PIN,相当于重新命名。
#define LED3_PIN GPIO_PIN_6  //定义GPIO_PIN_6引脚为LED3_PIN,相当于重新命名。
#define LED1_PORT GPIOE  //定义GPIOE端口为LED1_PORT,相当于重新命名。
#define LED2_PORT GPIOA  //定义GPIOA端口为LED2_PORT,相当于重新命名。
#define LED3_PORT GPIOA  //定义GPIOA端口为LED3_PORT,相当于重新命名。
//如果想用其他管脚控制LED,那么只需更改上面对应的端口与引脚编号即可
#define LED1_L GPIO_WriteLow(LED1_PORT,LED1_PIN);       //定义LED1_L,调用LED1_L命令,PE0引脚输出低电平
#define LED1_H GPIO_WriteHigh(LED1_PORT,LED1_PIN);      //定义LED1_H,调用LED1_H命令,PE0引脚输出低高平
#define LED1_R GPIO_WriteReverse(LED1_PORT,LED1_PIN);   //定义LED1_R,调用LED1_R命令,PE0引脚输出电平状态取反
#define LED2_L GPIO_WriteLow(LED2_PORT,LED2_PIN);       //定义LED2_L,调用LED2_L命令,PA3引脚输出低电平
#define LED2_H GPIO_WriteHigh(LED2_PORT,LED2_PIN);      //定义LED2_H,调用LED2_H命令,PA3引脚输出低高平
#define LED2_R GPIO_WriteReverse(LED2_PORT,LED2_PIN);   //定义LED2_R,调用LED2_R命令,PA3引脚输出电平状态取反
#define LED3_L GPIO_WriteLow(LED3_PORT,LED3_PIN);       //定义LED3_L,调用LED2_L命令,PA6引脚输出低电平
#define LED3_H GPIO_WriteHigh(LED3_PORT,LED3_PIN);      //定义LED3_H,调用LED2_H命令,PA6引脚输出低高平
#define LED3_R GPIO_WriteReverse(LED3_PORT,LED3_PIN);   //定义LED3_R,调用LED2_R命令,PA6引脚输出电平状态取反

/*---------------------------函数声明-----------------------------------*/
void LED_Init(void);  //LED初始化函数
void LED_Demo1(void); //闪烁例程1,间隔时间为1秒
void LED_Demo2(void); //闪烁例程2,间隔时间为1秒
#endif //定义文件名称结束

在这里我们对程序的主要部分做了详细的说明,如果想了解全部程序,请使用IAR软件打开我们的第11讲试验程序(位置以光盘内容为主)。

相关帖子

沙发
耶耶5555| | 2016-11-21 16:14 | 只看该作者

使用特权

评论回复
板凳
刘洋讲单片机|  楼主 | 2017-1-10 10:00 | 只看该作者

使用特权

评论回复
地板
龙帝728| | 2020-3-24 22:11 | 只看该作者
本帖最后由 龙帝728 于 2020-3-24 22:15 编辑

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

148

主题

499

帖子

61

粉丝