HSE、HSI、LSI 都可以作为系统主时钟源,STM8 单片机复位以后默认 HSI 的 8 分频作为系统主时钟,其原因是 HSI 稳定时间短,而其 8 分频又可以保证系统在较差的 VDD 条件下安全启动。在系统运行的过程中可以切换系统的主时钟源。系统时钟源的切换有 2 种方式:自动切换、手动切换。
自动切换可以使用最少的指令来完成主时钟源的切换,用户程序可以处理其他事物而不用关心确切的切换时间;手动切换在硬件准备好之后不立即切换,允许用户精确的控制切换发生的时间。
自动切换的步骤为:
1.设置切换控制寄存器 CLK_SWCR 中的 SWEN 位,使能时钟切换。
2.设置主时钟切换寄存器 CLK_SWR,选择目标时钟源。切换控制寄存器中的切换忙标志位 SWBSY被硬件置位,目标时钟源启动,原时钟源依旧驱动内核和外设。一旦目标时钟源稳定,主时钟切换寄存器 CLK_SWR 中的值将被复制到主时钟状态寄存器 CLK_CMSR 中。此时,SWBSY 位被硬件清零,目标时钟源替代原时钟源,CLK_SWCR 寄存器中的时钟切换中断标志位 SWIF 被硬件置位,如此时钟切换中断被使能,则会产生中断。
手动切换的步骤为:
1.设置主时钟切换寄存器 CLK_SWR,选择目标时钟源。SWBSY 位会被硬件置位,目标时钟源启动,原时钟源依然驱动内核和外设。
2.用户程序通过读取 CLK_SWCR 寄存器的 SWIF 位来等待目标时钟源准备就绪,SWIF 为 1 时代表目标时钟源准备就绪。如果时钟切换被使能,SWIF 被硬件置位时将触发中断。
3.如果目标时钟源准备就绪,则用户程序在预期的时间点将 CLK_SWCR 寄存器的 SWEN 位置位来进行时钟切换。
通过判断 SWIF 位可以判断时钟切换是否完成,通过读取 CLK_CMSR 寄存器可以得知当前的系统主时钟源。切换完成之后要写 SWIF 位为 0 你来清零此标志。
不管是自动切换还是手动切换,如果原时钟源在切换后仍然被其他模块使用,则原时钟源不会被自动关闭,需要通过配置内部时钟寄存器 CLK_ICKR 或外部时钟寄存器 CLK_ECKR 来关闭。
如果时钟切换没有成功,可以通过软件清零 SWBSY 位来复位当前切换,使 CLK_SWR 恢复原时钟源。
使用寄存器来切换时钟
自动切换和手动切换的流程图对比如图所示。
下面我们通过实例来掌握 STM8S 时钟切换的操作。本章节的实例设想开发板的 LED 分别在系统主时钟为 HSI/8、HSI、HSE/16、LSI 的时候各闪烁 5 次,系统主时钟的切换分别采用自动切换和手动切换,通过实验现象即可判断系统时钟切换是否成功。
void main( void )
{
unsigned char i;
InitLED();
//HSI 做主时钟源
//复位后默认主时钟为 16M/8=2M
for(i=0;i<5;i++)
{
PC_ODR=(0<<3);//PC_ODR 第 3 位清零,输出低电平
PE_ODR&=0xFE; //PE_ODR 第 0 位清零,输出低电平
PD_ODR&=0xF7;//PD_ODR 第 3 位清零,输出低电平
Delay(50000); //延时
PC_ODR=(1<<3);//PC_ODR 第 3 位置位,输出高电平
PE_ODR|=0x01; //PE_ODR 第 0 位置位,输出高电平
PD_ODR|=0x08;//PD_ODR 第 3 位清零,输出低电平
Delay(50000); //延时
}
CLK_CKDIVR=0x00;//HSI 不分频,主时钟 16M
for(i=0;i<5;i++)
{
PC_ODR=(0<<3);//PC_ODR 第 3 位清零,输出低电平
PE_ODR&=0xFE; //PE_ODR 第 0 位清零,输出低电平
PD_ODR&=0xF7;//PD_ODR 第 3 位清零,输出低电平
Delay(50000); //延时
PC_ODR=(1<<3);//PC_ODR 第 3 位置位,输出高电平
PE_ODR|=0x01; //PE_ODR 第 0 位置位,输出高电平
PD_ODR|=0x08;//PD_ODR 第 3 位清零,输出低电平
Delay(50000); //延时
}
//HSE 做主时钟源,自动时钟切换
CLK_SWCR|=0x02;//时钟切换启动,SWEN=1
CLK_SWR=0xB4; //选择目标时钟源,0xB4=HSE
while((CLK_SWCR&0x08)==0);//等待切换时间发生,此时 SWIF=1
CLK_SWCR&=0xF7;//清除切换标志
if(CLK_CMSR==0xB4)//判断主时钟源是否为 HSE
{
CLK_CKDIVR=0x04; //CPU 时钟频率为主时钟源 16 分频
for(i=0;i<5;i++)
{
PC_ODR=(0<<3);//PC_ODR 第 3 位清零,输出低电平
PE_ODR&=0xFE; //PE_ODR 第 0 位清零,输出低电平
PD_ODR&=0xF7;//PD_ODR 第 3 位清零,输出低电平
Delay(50000); //延时
PC_ODR=(1<<3);//PC_ODR 第 3 位置位,输出高电平
PE_ODR|=0x01; //PE_ODR 第 0 位置位,输出高电平
PD_ODR|=0x08;//PD_ODR 第 3 位清零,输出低电平
Delay(50000); //延时
}
}
//LSI 做主时钟源,手动时钟切换,请先在选项字节中使能 LSI
CLK_SWR=0xD2;//选择目标时钟源,0xD2=LSI
while((CLK_SWCR&0x08)==0);//等待目标时钟源准备就绪,此时 SWIF=1
CLK_SWCR&=0xF7;//清除切换标志
CLK_SWCR|=0x02;//进行切换
if(CLK_CMSR==0xD2)
{
for(i=0;i<5;i++)
{
PC_ODR=(0<<3);//PC_ODR 第 3 位清零,输出低电平
PE_ODR&=0xFE; //PE_ODR 第 0 位清零,输出低电平
PD_ODR&=0xF7;//PD_ODR 第 3 位清零,输出低电平
Delay(300); //延时
PC_ODR=(1<<3);//PC_ODR 第 3 位置位,输出高电平
PE_ODR|=0x01; //PE_ODR 第 0 位置位,输出高电平
PD_ODR|=0x08;//PD_ODR 第 3 位清零,输出低电平
Delay(300); //延时
}
}
while(1)
{
}
}
void InitLED(void)
{
PC_DDR|=0x08;//设置 PC3 为输出模式
PC_CR1|=0x08;//设置 PC3 为推挽输出
PC_CR2|=0x00;//设置 PC3 为 10MHz 快速输出
PE_DDR|=0x01;//设置 PE0 为输出模式
PE_CR1|=0x01;//设置 PE0 为推挽输出
PE_CR2|=0x00;//设置 PE0 为 10MHz 快速输出
PD_DDR|=0x08;//设置 PD3 为输出模式
PD_CR1|=0x08;//设置 PD3 为推挽输出
PD_CR2|=0x00;//设置 PD3 为 10MHz 快速输出
}
InitLED();函数为 LED 初始化函数,与前面章节的代码相同。
首先 HSI 做系统主时钟。系统复位后默认 HSI/8=2M 为系统主时钟;接着,通过修改 CLK_CKDIVR寄存器,使 HSI 不分频作为系统主时钟。
系统主时钟由 HSI 切换至 HSE 是通过自动切换方式实现的。首先置位 SWEN 位以启动时钟切换;然后设置 CLK_SWR 寄存器以选择目标时钟源为 HSE;通过读取 SWIF 位的值来判断系统时钟切换是否完成,如果 SWIF 为 1 则表示转换完成;转换完成之后要通过软件来写入 0 来清标志位 SWIF。
系统主时钟由 HSE 切换至 LSI 是通过手动切换的方式实现的。在编写程序之前需要先修改选项字节OPTION BYTE 以使能 LSI;程序中先设置 CLK_SWR 寄存器以选择目标时钟源为 LSI;通过读取 SWIF的值等待至 LSI 就绪之后清除 SWIF 位,接着置位 SWEN 位启动转换。
可以通过读取 CLK_CMSR 寄存器查看当前系统时钟源是否为预设的目标时钟源。
实验现象为 LED 以 4 种不同的频率闪烁,与预期的实验效果相符。
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』