STM32硬件SPI时钟频率与时钟解析(基于逻辑分析仪的抓包试验)
⾸先粘贴出我们CubeMX⽣成的时钟配置:
劝学知识点
然后启⽤SPI3的功能,这⾥因为博主的逻辑分析仪⽐较low,所以把SPI的波特率设置成最⼤分频,即256分频,此时CubeMX⼯具计算出来的时钟频率为1.5625MBits/s:
我们都知道,SPI3挂载在APB1总线上,受到总线的最⼤时钟120M的限制,由前⾯的时钟图可以知道,
APB1总线时钟速度为100M,那么经过256分频应该是390.625KHz才对。1.5625M/390.625K=4,这⾥的4倍频,是CubeMX软件计算的问题,还是真的哪⾥有了4倍频?
先研究⼀下⼿册⾥关于APB1寄存器的相关说明:
质量事故报告其中SPI3的时钟包含了给spi_ker_ck 输⼊的内核时钟,以及 rcc_pclk1总线接⼝时钟。
先看⼀下SPI的结构图:
菠萝蜜的营养
对于SPI外设来说,有两个时钟域,⼀个输⼊到寄存器、另外⼀个输⼊到时钟发⽣器,从上⾯的图可以简单看出来,spi_pclk给SPI寄存器提供访问时钟,⽽spi_ker_ck则是给SPI从设备提供SCK信号输出。
再回到CubeMX的软件配置界⾯,发现这⾥新激活了⼀块:
多了⼀个SPI时钟矩阵,难道这个就是spi_ker_ck?
假设这个最⼤再来分析⼀波,对于STM32的SPI协议来说,Data Size最⼩为4bit,最⼤为32bit;
当传输的数据位为最⼩4bit时:
受到APB1总线速度的限制,spi_pclk最⼤也就120MHz,因为PLLQ最⼤也只能是480MHz,假设这个SPI Clock MUX就是
spi_ker_ck,那么最⼤也就是480MHz,刚好接收完4个bit,寄存器的时钟脉冲也到了。
话不多说验证⼀波,根据CubeMX⽣成的系统时钟配置如下:
/**
* @brief System Clock 配置
* system Clock 配置如下:
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 400000000 (CPU Clock)
* HCLK(Hz) = 200000000 (AXI and AHBs Clock)塔顶上的猫
* AHB Prescaler = 2
* D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
* D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
* D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
* D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
* HSE Frequency(Hz) = 25000000
* PLL_M = 5
* PLL_N = 160
* PLL_P = 2
网上炒作* PLL_Q = 4
* PLL_R = 2
* VDD(V) = 3.3
* Flash Latency(WS) = 4
* @param None
* @retval None
*/
static void SystemClock_Config(void)
static void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
HAL_StatusTypeDef ret = HAL_OK;
/*使能供电配置更新 */
MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN,0);
/
* 当器件的时钟频率低于最⼤系统频率时,电压调节可以优化功耗,
关于系统频率的电压调节值的更新可以参考产品数据⼿册。 */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)){}
/* 启⽤HSE振荡器并使⽤HSE作为源激活PLL */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM =1;
电烤箱烤鱼的做法RCC_OscInitStruct.PLL.PLLN =100;
RCC_OscInitStruct.PLL.PLLP =2;
RCC_OscInitStruct.PLL.PLLR =2;
RCC_OscInitStruct.PLL.PLLQ =4;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
ret =HAL_RCC_OscConfig(&RCC_OscInitStruct);
if(ret != HAL_OK)
{
while(1){;}
}
/* 选择PLL作为系统时钟源并配置总线时钟分频器 */
RCC_ClkInitStruct.ClockType =(RCC_CLOCKTYPE_SYSCLK | \
RCC_CLOCKTYPE_HCLK | \
RCC_CLOCKTYPE_D1PCLK1 | \
RCC_CLOCKTYPE_PCLK1 | \
RCC_CLOCKTYPE_PCLK2 | \
RCC_CLOCKTYPE_D3PCLK1);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;留学咨询
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
ret =HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
if(ret != HAL_OK)
{
while(1){;}
}
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct ={0};
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC | RCC_PERIPHCLK_CKPER; PeriphClkInitStruct.CkperClockSelection = RCC_CLKPSOURCE_HSE;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_CLKP;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct)!= HAL_OK)
{
while(1){;}
}
}
对应SPI3的配置函数如下:
SPI_HandleTypeDef SPI3_Handler;//SPI2句柄
void SPI3_Init(void)
{
SPI3_Handler.Instance = SPI3;
SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;
SPI3_Handler.Init.CLKPha = SPI_PHASE_1EDGE;
SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; SPI3_Handler.Init.CRCPolynomial =7;
SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;上海英语培训学校
SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
HAL_SPI_Init(&SPI3_Handler);
__HAL_SPI_ENABLE(&SPI3_Handler);
}
//SPI2 读写⼀个字节
//TxData:要写⼊的字节
//返回值:读取到的字节
uint8_t SPI3_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&SPI3_Handler,&TxData,&Rxdata,1,1000); return Rxdata;//返回收到的数据
}
//读取芯⽚ID
//返回值如下:
//0XEF13,表⽰芯⽚型号为W25Q80
//0XEF14,表⽰芯⽚型号为W25Q16
//0XEF15,表⽰芯⽚型号为W25Q32
//0XEF16,表⽰芯⽚型号为W25Q64
//0XEF17,表⽰芯⽚型号为W25Q128
uint16_t W25QXX_ReadID(void)
{
uint16_t Temp =0;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
SPI3_ReadWriteByte(0x90);//发送读取ID命令
SPI3_ReadWriteByte(0x00);
SPI3_ReadWriteByte(0x00);
SPI3_ReadWriteByte(0x00);
Temp|=SPI3_ReadWriteByte(0xFF)<<8;
Temp|=SPI3_ReadWriteByte(0xFF);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
return Temp;
}
void W25QXX_Init(void)
{
MX_GPIO_Init();
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
SPI3_Init();
uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.