一、外部晶振起振失败导致串口波特率异常
型号:stm32f407 在开发过程中,原理样机由于布线、调理电路等设计问题,会出现外部晶振起振失败的情况。在stm32中,当外部晶振起振失败,将会启动内部高速晶振,并保持默认的时钟配置。 以hal库为例,具体代码在函数HAL_RCC_ClockConfig()中实现: 1、首先配置内部晶振,默认的分频系数(HSI 的信号直接提供给SYSCLK,均16分频):即系统时钟16M,外设时钟1M 2、开始初始化开发者真正的配置,这个配置已经存储在句柄RCC_ClkInitTypeDef中,首先是等待外部晶振起振(准备) 3、准备好后开始配置PLL、SYSCLK信号源以及外设时钟分配系数 4、最后更新部分数据(信息) 在这几个步骤中,若其中一个步骤失败就会返回错误,不再往下执行,若内部时钟初始化失败,将会直接申请硬件错误中断。若外部晶振起振失败则会保持默认配置(内部HSI,16分频)
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
{
...
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
{
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
}
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
}
assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
}
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
{
assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));
if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE)
{
if(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
{
return HAL_ERROR;
}
}
else if((RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK) ||
(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK))
{
if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
{
return HAL_ERROR;
}
}
else
{
if(__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)
{
return HAL_ERROR;
}
}
__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);
tickstart = HAL_GetTick();
while (__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos))
{
if ((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
}
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3U));
}
...
}
那么外部晶振起振失败是怎么影响串口波特率的呢?串口原本波特率配置为115200,对应时钟0.1M。起初在进行通信时发现数据的发送经常超时导致系统任务异常,进行数据量核对后,使用示波器观察输出的波形与配置不符合,认定波特率异常。接着在排除硬件电路问题后开始排查主控芯片时钟问题,通过以下函数: 1、HAL_RCC_GETSysClockFreq(); //获取系统时钟 2、HAL_RCC_GetPCLK2Freq(); //获取高速外设总线时钟(串口1挂载在此)
获得数据与此前分析的一致,16M的主频,1M的外设时钟。按照计算的公式baud = Fclk/8*(2-OVER8)USARTDIV 1M/82*115200=USARTDIV=0.54(16倍过采样) 那么寄存器BRR应该装载0x09((0<<4)+0.54 * 16) 查看BRR寄存器数据,确实为0x09
既然波特率配置得没有问题,为什么会出错呢?我初步怀疑波特率过高,将波特率下降,发现确实波特率变正常了,但是不甚理解,按理来说1M的时钟可以驱动115200的波特率,进行一番思索,将原因归结在过采样的配置上,由于为了保证数据的可靠性,一般串口设备都会进行过采样的配置,由于此前配置为16倍的过采样,那么主控芯片需要用1.6M左右的时钟才能完成,而当前的时钟配置不满足需求,故导致波特率异常。
知道原因后,可以对几处进行修改来修正波特率: 1、最优解是外部晶振起振(检查晶振、外围电路)。 2、临时调试可以启用内部高速晶振配置高的时钟。 3、懒得配置的可以将上述代码中的RCC_HCLK_DIV16改为RCC_HCLK_DIV1,即不进行分配,让外设时钟为16M。 4、最次之可以降低波特率或者使用8倍过采样。
|