项目案例:按键控制主时钟源的切换
硬件资源:1个LED灯,1个按键。
D3灯 <——– PC1
SW3按键—->PB0
硬件电路相关信息,详见:XMF06A开发的电路结构框图。
功能要求:
步骤1:系统上电后,采样默认的主时钟源,即HSI的8分频,2MHz。
步骤2:D3灯亮,过一小会,D3灯熄灭,再过一会。
步骤3:读取按键SW3引脚的输入状态。如果有按键按下,等待按键松开后,进行主时钟源的手动切换。在按键的按下过程中,不能影响D3灯的闪烁功能。在进行主时钟源切换的时候,先判断当前的主时钟源状态。如果为内部高速RC振荡器HSI,则切换为高速外部晶体振荡器HSE,反之,切换为内部高速RC振荡器HSI。
步骤4:重复步骤2。
STM8关于时钟控制器的知识要点
主时钟的时钟源有4种 :
<1> 1-24MHz高速外部晶体振荡器(HSE)
<2> 最大24MHz高速外部时钟信号(HSE user-ext)
<3>16MHz高速内部RC振荡器(HSI)
<4> 128KHz低速内部RC振荡器(LSI)
各个时钟源可以单独打开或关闭,从而优化功耗。
主时钟的时钟源切换方式有2种:自动切换和手动切换。
当发生主时钟源切换事件和CSS事件时,时钟控制器可产生中断。
自动切换:用户可使用最少的指令完成时钟源的切换,应用软件可继续其他操作而不用考虑切换事件所占的切确时间。基本流程如下:
<1> 设置切换控制寄存器(CLK_SWCR)中的SWEN位,使能切换机制。
<2> 向主时钟切换寄存器(CLK_SWR)写入一个8位的值,用以选择目标时钟源。寄存器CLK_SWCR中的SWBSY位被硬件置位,目标源振荡器启动。原时钟源依然被用于驱动内核与外设。
<3> 一旦目标时钟源稳定,寄存器CLK_SWR中的值将被复制到主时钟状态寄存器(CLK_CMSR)中。此时,SWBY位被清除,新的时钟源替代旧的时钟源。
<4> 寄存器CLK_SWCR中的标志位SWIF被置位,如果此时SWIEN位为1,则会产生一个中断。
手动切换:虽然不能够立即切换,但它允许用户精确地控制切换事件发生的时间。 基本流程如下:
<1> 向主时钟切换寄存器(CLK_SWR)写入一个8位的值,用以选择目标时钟源。寄存器CLK_SWCR中的SWBSY位被硬件置位,目标源振荡器启动。原时钟源依然被用于驱动内核与外设。
<2> 用户软件需要等待至目标时钟源稳定。寄存器CLK_SWCR中的标志位SWIF用以指示目标时钟源是否已稳定,如果SWIEN位为1,则会产生一个中断。
<3> 最后,由用户软件在所选的时间点,设置寄存器CLK_SWCR中的SWEN位,执行切换。
时钟控制涉及的寄存器有13个,但常用的主要有以下6个:
主时钟状态寄存器:CLK_CMSR
主时钟切换寄存器:CLK_SWR
切换控制寄存器:CLK_SWCR
时钟分频寄存器:CLK_CKDIVR
外设时钟门控寄存器1:CLK_PCKENR1
外设时钟门控寄存器2:CLK_PCKENR2
寄存器的具体信息详见:【STM8开发速查】时钟控制常用寄存器定义与应用参考。
项目案例的基本实现思路
首先,程序的整体框架搭好,实现D3灯的循环闪烁功能。
其次,在按键扫描程序中,处理好按键按下的时候,D3灯循环闪烁正常的情况。这是一个非常重要的小小细节,也是初学者的一个难点。
最后,在按键松开后,实现主时钟的手动切换。在这里要注意一下,在进行时钟切换之前,先要判断当前是不是正在进行时钟的切换。如果正在进行时钟切换,则等待其完成之后,再开始你的主时钟切换。
案例的参考源码及注释
#include "iostm8s105k6.h"
#define D3 PC_ODR_ODR1
#define SW3 PB_IDR_IDR0
/*=========================普通延时函数==========================*/
void Delay(unsigned int t)
{
while(t--);
}
/*========================GPIO端口初始化=========================*/
void Init_GPIO()
{
//初始化按键SW3的PB0引脚
PB_DDR &= ~0x01; //PB0设置为输入模式
PB_CR1|= 0x01; //PB0设置为带上拉电阻输入
PB_CR2&= ~0x01; //PB0禁止外部中断
//初始化D3灯的PC1引脚
PC_DDR |= 0x1e; //PC1、PC2、PC3、PC4设置为输出模式
PC_CR1|= 0x1e; //PC1、PC2、PC3、PC4设置为推挽输出
PC_CR2&= ~0x1e; //PC1、PC2、PC3、PC4输出速度最大为2MHz
}
/*========================LED灯闪烁函数==========================*/
void LED_Flicker()
{
D3 = 1; //点亮D3灯
Delay(60000);
D3 = 0; //熄灭D3灯
Delay(60000);
}
/*==================手动将主时钟源从HSI切换到HSE=================*/
void Clock_HSI_to_HSE()
{
while(CLK_SWCR_SWBSY == 1); //等待当前主时钟切换完成
CLK_SWCR_SWEN = 0; //禁止时钟切换
CLK_SWR = 0xB4; //选择主时钟源为HSE外部晶振
while(CLK_SWCR_SWIF == 0); //等待目标时钟源稳定就绪
CLK_SWCR_SWEN = 1; //使能时钟切换
}
/*==================手动将主时钟源从HSE切换到HSI=================*/
void Clock_HSE_to_HSI()
{
while(CLK_SWCR_SWBSY == 1); //等待当前主时钟切换完成
CLK_SWCR_SWEN = 0; //禁止时钟切换
CLK_SWR = 0xE1; //选择主时钟源为HSI内部振荡器
while(CLK_SWCR_SWIF == 0); //等待目标时钟源稳定就绪
CLK_SWCR_SWEN = 1; //使能时钟切换
}
/*===========================按键扫描函数========================*/
void Scan_Keys()
{
if(SW3 == 0) //监测SW3的输入信号
{
Delay(100); //按键去抖动处理
if(SW3 == 0) //确认为SW3按键触发信号
{
while(SW3 == 0) //等待按键松开
{
LED_Flicker();
}
if(CLK_CMSR ==0xE1) //当前主时钟源为HSI
{
Clock_HSI_to_HSE(); //将主时钟切换为HSE外部晶振
}
else if(CLK_CMSR ==0xB4) //当前主时钟源为SHE
{
Clock_HSE_to_HSI(); //将主时钟切换为HSI内部振荡器
}
}
}
}
/*==========================主函数===============================*/
void main()
{
Init_GPIO(); //初始化D3灯和按键SW3的GPIO引脚
while(1)
{
LED_Flicker(); //D3灯循环闪烁
Scan_Keys(); //轮询扫描按键输入状态
}
}