STM32 - 3

STM32 - 3

时钟和USB接口

时钟

一些缩写

  • 外部高速时钟:HSE
  • 内部高速时钟:HSI
  • 时钟和复位控制:RCC(Reset and Clock Control)

STM32的时钟系统简介

image-20220801172808525

这个图是官方手册的时钟树的简化版,为了便于理解,可以从中间的SYSCLK,把整个时钟树分为两部分:左边为时钟源,右边为片上外设。

系统时钟来源选择

可以看到SYSCLK旁边的那个梯形,就是一个选择器,由他来选择STM32究竟用哪个时钟源。HSE、HSI就不再多说,中间的那个PLL叫锁相环,用来做倍频的,就是把HSI或HSE做一定的倍频之后再作为整个片上外设的时钟输入。

片上外设时钟

SYSCLK右边,会首先过一个AHB总线进行分频,把输入的时钟频率分成1/n,变成了HCLK。然后HCLK一分为二, 去供给片上外设。其中APB1的最大频率比APB2低一些,即APB2的时钟的最高频率搞一些,功耗也高一些,适合高频的片上外设。

  • APB1和APB2是两条总线,都挂载在AHB总线上
  • 驱动Cortex-M3内核的时钟是HCLK

STM32时钟编程接口

API

首先了解两个API:

1
2
3
4
5
// 配置时钟来源,即上面图中的左半部分
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);

// 配置SYSCLK、HCLK、PCLK1、PCLK2时钟,即上面图中的右半部分
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t Flatency);

时钟配置

首先,我们需要去配置时钟来源。从上面的API可以看到,配置时钟源需要一个RCC_OscInitTypeDef,其定义可以去看源码,现在我们就以外部时钟源+9倍频锁相环为例,看看怎么配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
RCC_OscInitTypeDef oscInit;
// 配置时钟类型为HSE
oscInit.OscillatorType = RCC_OSCILLATORTYPE_HSE;
// 配置HSE开启或关闭
oscInit.HSEState = RCC_HSE_ON;
// 配置打开锁相环PLL
oscInit.PLL.PLLState = RCC_PLL_ON;
// 设置PLL的时钟来源
oscInit.PLL.PLLSource = RCC_PLLSOURCE_HSE;
// 设置PLL的倍频倍数为9倍频
oscInit.PLL.PLLMUL = RCC_PLL_MUL9;

// 设置时钟源
HAL_RCC_OscConfig(&oscInit);

设置完时钟源之后,就可以设置后续的时钟了,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
RCC_ClkInitTypeDef clkInit;
// 被设置的时钟类型,我们选择SYSCLK和HCLK
clkInit.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK;
// 设置SYSCLK等于PLL时钟源
clkInit.SYSCLKSource = RCC_SYSCLKSOURCE_PLL
// 通过设置AHB分频器分频倍率为1,来设置HCLK=SYSCLK
clkInit.AHBCLKDivider = RCC_SYSCLK_DIV1;

// 最后设置时钟
HAL_RCC_ClockConfig(&clkInit, FLASH_LATENCY_2);

最后一个参数的FLASH_LATENCY配置,实际上和你设置的时钟SYSCLK有关系,具体多少频率的时钟对应多少FLASH_LATENCY需要去库里面的代码里看注释。

到这里,时钟就配置完了。更多的时钟配置可以去看HAL库的源代码手册。

USB

USB介绍

首先需要介绍下usb的基本概念和参数,这对于在stm32上使用usb设备有很大的帮助。

USB默认是个4pin设备,其中中间的D+和D-分别会接一个15K的下拉电阻:

image-20220812140819445

所以,默认D+和D-默认都是低电平。然而,在USB插头上面,D+会接一个1.5K的上拉电阻,这样的话一旦插入USB设备,D+就会被拉到高电平,产生一个中断表名设备已连接。

USB分为主机和设备,所有的通信都是由主机(比如PC)发起的,输入和输出也都是针对主机而言。

USB的传输类型

一共4种:

  1. 控制:一般是用于初次连接的初始化,比如分配地址、获取USB设备信息等
  2. 中断:主机会定时询问是否有数据,这就是鼠标、键盘等设备的通信模式
  3. 批量:大文件传输
  4. 同步:数据同步,比如摄像头

USB供电

USB可以给设备供电,这是通过VBUS来供电的。USB2.0给低功耗设备供电电流最多可以100ma。对于高功耗设备,最高不超过500ma。

USB配置描述符

上面说的这些配置,比如是否低功耗设备等,都是在最初连接的时候,设备通过USB配置描述符告诉主机的。下面是一个例子,可以参考下。具体所有的描述符建议去USB的官方查看官方文档:

image-20220812141723154

USB HID

USB支持很多种设备类型,我们主要会用到HID,即Human Interface Device。

USB挂起模式

当USB主机连续3ms没有检测到USB设备有信号,就自动进入USB挂起模式。因此,为了避免进入挂起模式,设备需要周期性地发送Keep Alive或者SOF信号。

HID键盘的键值

对于USB HID设备来说,其键盘的数据包包含8个字节。基本上可以分为3部分:

  • Byte0:功能键,如L/R-Ctrl、Shift、Alt、GUI等
  • Byte1:保留
  • Byte2-7:普通按键

对于普通按键来说,HID设备已经规定好了各个按键的键值,比如右箭头的键值为79(十六进制0x4F)。如果要向主机发送一个右箭头按键,那么在Byte2写个0x4F就行了。

STM32的USB配置

默认情况下,STM32CubeMX生成的USB HID设备是鼠标,需要修改配置描述符来修改为键盘的键值。首先看一下STM32CubeMX配置USB的界面:

image-20220813155212053

左下角可以看到F411是USB_OTG_FS,意思是带OTF的全速USB设备。中间可以看到相关的配置项,对于键盘设备一般选Device Only即可。下面是休眠和供电的选项,选不选都可以。

然后,在middleware下选择USB_DEVICE,就可以看到作为USB设备的相关配置:

image-20220813155505269

中间的Class For FS IP就选择HID设备,下面的Device Descriptor设备描述符,就可以配置这个USB设备连接到电脑上之后的一些信息,比如厂家、设备名等等。然后生成代码即可。

默认情况下,CubeMX生成的HID设备是鼠标。在生成的代码中,找到usbd_hid.c文件,找到这段代码:

image-20220813160051260

可以看到有一个nInterfaceProtocol,在默认情况下值为0x02,即mouse。对于键盘我们把它改成0x01即可。此外,在下面还有一个HID_MOUSE_REPORT_DESC_SIZE常量,指的是鼠标的回报描述符的大小,我们也需要把它改为键盘的大小:

image-20220813163009324

需要注意的是,这个DESC_SIZE有两处,都需要改为键盘。

改完上面三个地方之后,我们的代码就可以作为键盘的USBHID设备连接到电脑上了。

数据传输

最后的最后,在代码中只需要调用

1
USBD_HID_SendReport(&hUsbDeviceHS,  data, data_length);

即可向USB主机端发送数据。当然,data和data_length都必须符合USB的描述以及配置。具体请参见USB的文档:https://www.usb.org

updatedupdated2024-05-102024-05-10