首先粘贴出我们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)
{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.CLKPhase = 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.  printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
}void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(spiHandle->Instance==SPI3){__HAL_RCC_SPI3_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_2;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);}
}

先直接调用函数W25QXX_Init,得到输出:

连接逻辑分析仪:

复位一次抓包:

对照着我们读ID的程序:

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;
}

其中0x90=144,0xEF=239,0x14=20可以看出,读写过程是一个全双工的过程。

放大来看,左边有个ERROR是因为我是抓的片选信号的下降沿,而在上电初始化IO口的时候默认拉低,随后手动拉高了。
而右边的数据,时钟周期是1.25us,即800KHz,虽然逻辑分析仪比较垃圾,但是800KHz和签名算出来的1.5625MBits/s的波特率,好像还是差了个二倍关系呀,emm……
心中掠过千万种不合实际的想法:比如SPI可以根据时钟极性配置成上升沿和下降沿各采集一次?还是全双工的波特率要x2?但串口人家也没x2呀……

再次想着是不是逻辑分析仪的问题,准备把SCK速率调低,不过最大也就256分频了,再调就只能改PLL的DIV1Q了,说改就改,突然发现,写的代码里居然和时钟树不一样……(因为这里偷懒,直接把SPI的配置放在了一个常用的工程里,时钟配置直接用就没改,平时也没注意PLLQ……)

所以这里的实际计算的波特率应该是1.5625/2=781.25KHz,和上图里的800KHz接近。

总结两点:

  • HAL_SPI_TransmitReceive的传输是全双工的,如果硬件是单双工的收(发送端悬空),SPI3_ReadWriteByte可以传入任意参数;如果硬件是单双工的发,那么返回值可以不要。
  • SPI的有两个速率,APB1的速率只与处理器访问APB1总线上的SPI寄存器有关,不影响通信;而SPI的SCK速率由单独的时钟矩阵选择输入源,在经过SPI外设的分配器产生波特率。

希望这篇博文能够帮助大家更好的理解SPI协议。

STM32硬件SPI时钟频率与时钟解析(基于逻辑分析仪的抓包试验)相关推荐

  1. 嵌入式开发-STM32硬件SPI驱动TFT屏

    嵌入式开发-STM32硬件SPI驱动TFT屏 这次用到的TFT屏 CubeMX设置 代码编写 增加的内容 需要注意问题 代码下载 这次用到的TFT屏 现在的TFT屏幕已经很便宜了,65536色屏幕,2 ...

  2. STM32硬件SPI控制TM1638 按键数码管LED显示模块

    STM32硬件SPI控制TM1638按键数码管LED显示模块   从淘宝买来的,TM1638专门是控制LED的,LED组合起来就可以变成数码管,还有按键,这个我就没管了,不想管了,发这个帖子只是为了记 ...

  3. STM32硬件SPI驱动OLED

    文章目录 一.OLED相关 1.OLED简介 2.0.96寸的OLED模块概述 3.模块引脚说明 4.汉字点阵编码原理 二.硬件SPI 1.SPI简介 2.SPI的引脚映射关系 三.SPI驱动的OLE ...

  4. Java抓包分析四(基于jnetpcap进行抓包)——分析Http请求数据包

    在上篇文章中Java抓包分析三(基于jnetpcap进行抓包)--抓取Http请求数据包,我们讲解了TCP三次握手的过程和如何抓取Http数据包,但是我们并没有进行一个数据分析,接下来这篇文章我们将要 ...

  5. 接上篇ADN8810的STM32硬件SPI驱动时钟极性问题

    遇到的问题beij 背景:时钟极性(Clock Polarity)CPOL,默认为LOW. 遇到的问题,首先我是直接给了一个CODE让AND8810能够输出电流,然后按照数据手册给的公式把电流转为CO ...

  6. 基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522

    目录 1. 实验硬件及原理图 1.1 RFID硬件 1.2 硬件原理图 2. 单片机与RFID硬件模块分析 3. 利用STM32CubeMX创建MDK工程 3.1 STM32CubeMX工程创建 3. ...

  7. FM1722/02NL+STM32硬件SPI调试笔记

    1.介绍 FM17XX 系列通用非接触读卡机芯片是复旦微电子股份有限公司设计的,基于 ISO14443 标准的系 列通用非接触卡读卡机芯片,采用 0.6 微米 CMOS EEPROM 工艺. FM17 ...

  8. stm32硬件SPI驱动3线SPI-LCD的方法

    1.基本控制原理 三线SPI LCD, 顾名思义,最少只需要3个IO控制LCD显示,如果采用硬件控制上电时序和背光,最少只需要接SCK,CS,MOSI三个引脚即可控制LCD,并且不管接不接其他引脚,控 ...

  9. stm32 硬件spi半双工三线的一些研究心得

    a7105可以使用四线spi,或者3线spi, 但是之前都是使用3线的软件模拟的三线spi的,所以不想改其它代码了,就想可以提高一个spi的读写速度,原来软件方式的读写速度,在48Mhz的03x下面, ...

  10. STM32硬件SPI使用与nSS的理解

    最近在调试硬件SPI,之前使用软件模拟方式,为了提高速度,选择该方式进行替换. 花了一些时间了解寄存器跟库函数直接调用,结果要么读取不出数据(陷入等待TXE,RXNE死循环)或读取数据为0xffff, ...

最新文章

  1. seaborn使用boxplot函数进行箱图可视化(使用色彩调色板自定义设置箱图的颜色、在boxplot函数内设置palette参数自定义调色板)
  2. Java基础加强总结(三)——代理(Proxy)
  3. 微信小程序教学第二章(含视频):小程序中级实战教程之预备篇 - 封装网络请求及 mock 数据...
  4. 计算机科学速成课36:自然语言处理
  5. 前端学习(2365):图片的预览
  6. 小丑马戏团风格英文404网页模板
  7. 华为P50系列再曝“坏消息”:疑似再度延期至7月份
  8. VC++中字符串编码的转换
  9. 【数值分析】—— 对数函数、指数函数(数值稳定性)
  10. VS2010与.NET4系列 6.ASP.NET,HTML,JavaScript片断支持
  11. css渐变颜色php,CSS3中的颜色值RGBA以及渐变色的具体详解(图)
  12. Atitit mysql insert perf enhance 批量插入数据库性能 目录 1.1. 案一:使用ignore关键字 1 2. 异步插入 2 2.1. 其它关键:DELAYED  做为
  13. python设置excel格式_Python使用xlwt写excel并设置写入格式
  14. iOS游戏开发梦想成真:神奇的GameSalad
  15. 使用cgo,由于内存释放导致内存无效,引起的http crash
  16. 英语面试自我介绍java_java面试英语自我介绍
  17. 【产业互联网周报】阿里增持菜鸟;微信支付宝向外国游客开放服务;百度推“区块链+智能城市”建设...
  18. KDD 2020阿里巴巴论文一分钟秒读
  19. XXljob 使用教程(springboot)
  20. 磨金石教育摄影技能干货分享|古风人像拍摄要注意哪些问题

热门文章

  1. getStyle(getComputedStyle currentstyle) 获取非行间样式函数封装
  2. 人事经理常问的面试问题及答案
  3. 解决Rabbitmq连接超时问题
  4. 家用简单电线路图_这6张图在手,简单的家庭电路,电线还怕不会装?不存在的!...
  5. 客户端软件升级一般方案
  6. Outliers: The Story of Success
  7. pythonhello world欢迎某某某同学_【第四章】python基本语法学习与练习题(慕课网习题)...
  8. cpufreq schedutil原理剖析
  9. 全国2012年1月高等教育考试
  10. 分析力学-清华大学基科班课件