最近有个小东西用到STM8S来驱动,之前用STM8S使用的是内部自带的16MHz RC时钟,尚未尝试过使用外部晶振作为主时钟。今天记录一下使用外部晶振时遇到的一个问题。
老规矩,在进行程序设计时,首先初始化时钟,本次使用的是STM8S103F3P6芯片,8MHz的外部晶振,为了方便内外部时钟切换,结合资料写了两个切换函数
/***********************************************
*描述:采用内部16MHz的RC时钟为主时钟
* 初始化时钟为1分频 16M,无外部晶振
*版本:V1.0
*作者:
************************************************/
void HsiInit(void)
{
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
}
/***********************************************
*描述:采用8M外部晶振为主时钟,用于时钟切换
*版本:V1.0
*作者:
************************************************/
void HseInit(void)
{
CLK_DeInit(); //时钟初始化
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1); //8MHz,不分频
CLK_HSECmd(ENABLE); //外部时钟开
CLK_HSICmd(ENABLE); //内部RC开
while(SET != CLK_GetFlagStatus(CLK_FLAG_HSERDY)); //等待外部晶振起振
CLK_ClockSwitchConfig(CLK_SWITCHMODE_MANUAL,CLK_SOURCE_HSE,DISABLE,CLK_CURRENTCLOCKSTATE_DISABLE);
CLK_ClockSwitchCmd(ENABLE); //开始切换
}
在主函数调用所需的时钟函数即可,为了验证外部时钟是否切换成功,我在定时器1的通道1输出PWM进行验证,所幸得到了想要的波形,定时器1的配置如下
/***********************************************
*描述:TIM1CH1(PC6)输出PWM,频率为1K
* 定时器时钟为外部的8MHz
*版本:V1.0
*作者:
************************************************/
void TIM1_Init(void)
{
TIM1->EGR = 0x01;//重新初始化定时器1
TIM1->CR1 = 0x00;//边沿对齐方式,向上计数,发生更新事件,计数器不停止更新
TIM1->RCR = 0x00;//重复计数器的值为0,计时时间到,重复多少次产生中断,该值是重复多少次那个值
//设定预分频为,80分频 8M
TIM1->PSCRH =0x00; //PWM的时钟 影响周期
TIM1->PSCRL =0x4f; //80分频,TIM1的时钟为100K
TIM1->ARRH = 0x00; //设定重装载值
TIM1->ARRL = 0x63; //计数100次,等于T,即1ms(f=1K),每10us计一个数
//CH1
TIM1->CCER1 = 0x01; //CC2ER1开启CH1,CC1配置入输出,低电平有效
TIM1->CCMR1 = 0x68; //PWM模式1,使能预装载
//设置占空比
TIM1->CCR1H = 0x00;
TIM1->CCR1L = 0x0A; // 占空比值
TIM1->OISR &= ~0x03;
TIM1->BKR |= 0x80; //刹车
TIM1->CR1 |= 0x01; //使能TIM1计数器
// GPIO_Init(GPIOC, GPIO_PIN_6, GPIO_MODE_OUT_PP_LOW_FAST);
GPIOC->DDR|=0xff;//输出
GPIOC->CR1|=0xff;//推挽
GPIOC->CR2|=0xf0;//速度
}
接下来开始进入本次主题,采用8MHz外部时钟时,串口1传输数据异常。开始时串口初始化函数如下,使用波特率是9600
void Uart1_Init(u32 Baudrate)
{
UART1_DeInit();
UART1_Init((u32)Baudrate, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO, UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
UART1_Cmd(ENABLE ); //启用串口
}
如果使用的是内部的16MHz时钟,这三行初始化代码应该可以启动串口传输数据,但是我发现数据在传输的过程中出错,刚开始也怀疑是串口配置出错,假如配置出错,理论上是不会收到数据的,上面的代码能在内部时钟下正常传输数据,在外部时钟下传输出错,那很大可能就是波特率出现了问题。
串口初始化的波特率是9600,串口调试助手在9600的波特率下得不到正确的数据,printf打印出来的是乱码
啒f槝骧槝灅?榾ff啒f槝骧槝灅?榾ff啒f槝骧
槝灅?榾ff啒f槝骧槝灅?榾ff啒f槝骧槝灅?榾ff
啒f槝骧槝灅?榾ff啒f槝骧槝灅?榾ff啒f槝骧
槝灅?榾ff啒f槝骧槝灅?榾ff啒f槝骧槝灅?榾ff
啒f槝骧槝灅?榾ff啒f槝骧槝灅?榾ff啒f槝骧
槝灅?榾ff啒f槝骧槝灅?榾ff啒f槝骧槝灅?榾ff
当我把串口调试助手的波特率改为4800后,神奇的一幕出现了
ART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
UART1 is OK!
为什么呢?很好奇发生了什么,我把波特率两个寄存器值打印出来
UART1->BRR2: 2
UART1->BRR1: 68
UART1->BRR2: 2
UART1->BRR1: 68
UART1->BRR2: 2
UART1->BRR1: 68
UART1->BRR2: 2
UART1->BRR1: 68
结合STM8S参考手册,将这两个寄存器的值还原,得到UART_DIV = 0x0682,十进制就是UART_DIV = 1666,按照公式:
UART_DIV = F / Baudrate,其中F是主时钟频率,Baudrate是波特率,为9600
那么主时钟频率 = 1666 * 9600 = 15993600,接近16MHz,通过上面可以观察到,UART_DIV = 1666对应的是16MHz,那么8MHz应该为多少?
UART_DIV = 8000 000 / 9600 = 833.3,即UART_DIV = 0x0341
参考数据手册的设置规则,BRR1和BRR2的值应该为
UART1->BRR2 = 0x01;
UART1->BRR1 = 0x34;
所以串口初始化函数应该为(注意必须是先设置BRR2,再设置BRR1)
void Uart1_Init(u32 Baudrate)
{
UART1_DeInit();
UART1_Init((u32)Baudrate, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO, UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
UART1->BRR2 = 0x01;
UART1->BRR1 = 0x34;
UART1_Cmd(ENABLE ); //启用串口
}
此时,再将串口调试助手的波特率设置为9600,可以得到如下数据
UART1->BRR2: 1
UART1->BRR1: 34
UART1->BRR2: 1
UART1->BRR1: 34
UART1->BRR2: 1
UART1->BRR1: 34
UART1->BRR2: 1
UART1->BRR1: 34
UART1->BRR2: 1
UART1->BRR1: 34
UART1->BRR2: 1
UART1->BRR1: 34
UART1->BRR2: 1
UART1->BRR1: 34
至此,串口调试完成,引起传输数据出错,应该是库函数计算波特率是以内部的16MHz频率作为标准,假如你要用到其他频率,需要重新设置BRR1和BRR2这两个寄存器的值。
上一篇:STM8S的按键PWM调光灯历程
下一篇:使用STM8S003K3 ADC简介以及初始化
推荐阅读最新更新时间:2024-10-28 09:26