炸鸡派-基础测试例程
基础例程
代表非freertos代码,仅测试用。
功能就是亮灯、串口、按键。
按一下亮不同灯,串口打印不同数据。
接开发板的USB口会提示插入了USB设备。出现该U盘磁盘。
main函数代码
int main(void)
{/*用户代码开始*//* USER CODE BEGIN 1 *//* USER CODE END 1 *//*MCU配置*//* MCU Configuration--------------------------------------------------------*//*重置所有外设,初始化flash接口和滴答定时器*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//*配置系统时钟*//* Configure the system clock */SystemClock_Config();/*用户系统初始化*//* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//*初始化全部配置外设*//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_USB_DEVICE_Init();/*用户代码开始*//* USER CODE BEGIN 2 *//* USER CODE END 2 *//*无限循环*//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(blink_mode == 0){HAL_GPIO_TogglePin(LED_T_GPIO_Port, LED_T_Pin);printf("mode0\r\n");HAL_Delay(500);}else if(blink_mode == 1){HAL_GPIO_TogglePin(LED_T_GPIO_Port, LED_T_Pin);printf("mode1\r\n");HAL_Delay(250);}else if(blink_mode == 2){HAL_GPIO_TogglePin(LED_T_GPIO_Port, LED_T_Pin);printf("mode2\r\n");HAL_Delay(100);}else if(blink_mode == 3){HAL_GPIO_WritePin(LED_T_GPIO_Port, LED_T_Pin, GPIO_PIN_RESET);printf("close\r\n");HAL_Delay(500);}}/* USER CODE END 3 */
}
代码分析
HAL_Init
/*配置flash的 预取值,指令缓存,数据缓存*/
/*设置中断优先级分组,分组4代表 0位子优先级、4位抢占优先级*/
/*使用SysTick作为时间基准源,并配置1毫秒的滴答时间
/*初始化低速硬件*/
HAL_StatusTypeDef HAL_Init(void)
{/*配置flash的预取值,指令缓存,数据缓存*//* Configure Flash prefetch, Instruction cache, Data cache */ /*指令缓存*/
#if (INSTRUCTION_CACHE_ENABLE != 0U)__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
#endif /* INSTRUCTION_CACHE_ENABLE *//*数据缓存*/
#if (DATA_CACHE_ENABLE != 0U)__HAL_FLASH_DATA_CACHE_ENABLE();
#endif /* DATA_CACHE_ENABLE *//*预取指*/
#if (PREFETCH_ENABLE != 0U)__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE *//*设置中断优先级分组,分组4代表 0位子优先级、4位抢占优先级*/HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);/*使用SysTick作为时间基准源,并配置1毫秒的滴答时间(复位后的默认时钟是HSI)。内部告诉时钟为16Mhz*/HAL_InitTick(TICK_INT_PRIORITY);/*初始化底层硬件*/HAL_MspInit();/* Return function status */return HAL_OK;
}
Flash配置
预取指、指令缓存、数据缓存。

这里几个配置就是给FLASH的ACR寄存器置位。
/*指令缓存*/
#if (INSTRUCTION_CACHE_ENABLE != 0U)__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
#endif /* INSTRUCTION_CACHE_ENABLE *//*数据缓存*/
#if (DATA_CACHE_ENABLE != 0U)__HAL_FLASH_DATA_CACHE_ENABLE();
#endif /* DATA_CACHE_ENABLE *//*预取指*/
#if (PREFETCH_ENABLE != 0U)__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */
预取指是一种机制,用于在实际需要数据之前提前从内存中加载数据到缓存中。
指令缓存是一种高速缓存,用于存储最近或频繁使用的指令。
数据缓存用于存储最近或频繁使用的数据。
设置中断优先级分组
断言失败,程序会被中断。
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{/* 断言宏,检查传入的参数是否有效 */assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));/* 设置优先级分组*/NVIC_SetPriorityGrouping(PriorityGroup);
}
嵌套向量中断控制器(NVIC)集成在Cortex-M0处理器里,它与处理器内核紧密相连,并且提供了中断控制功能以及对系统异常的支持。
除了NVIC,系统控制块(SCB)位于系统控制空间(SCS)地址区域。

设置Systick时间基准源
这里传入的 tick中断优先级为15U
最后就是将Systick中断优先级号配置为-1,
优先级配置为15(最低优先级),并设置重载值、初始值,
重载值(溢出值)设为16000-1,此时系统时钟为16Mhz即每1ms发生一次中断。
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{/* 配置Systick为1ms的时间基准*/16000000 / (1000/1)if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U){return HAL_ERROR; //配置寄存器成功返回的是0,就不会到这里}/* 配置Systick中断优先级 */if (TickPriority < (1UL << __NVIC_PRIO_BITS)) //1UL<<4{/*配置systick中断号-1,tick优先级15,子优先级 0*/HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);uwTickPrio = TickPriority;}else{return HAL_ERROR;}/* Return function status */return HAL_OK;
}
/*初始化Systick定时器,使其以特定的频率产生中断*/
/*给到这里的参数是16000*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{/*检查重载值是否超出最大范围*/if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk){return (1UL);}/*设置重载寄存器*/SysTick->LOAD = (uint32_t)(ticks - 1UL);/*设置中断优先级*/NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /*加载计数器值*/SysTick->VAL = 0UL; /*中断使能使能*/ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | //选择时钟源SysTick_CTRL_TICKINT_Msk | //使能中断SysTick_CTRL_ENABLE_Msk; //使能定时器 return (0UL);
}
使能SYSCFG和PWR时钟
初始化SYSCFG的时钟和APB2高速外设的时钟。
通讯线一般为高速如ADC、USART、SPI等,其最大频率通常为72MHz。
初始化PWR的时钟和APB1低速外设的时钟。
如I2C、USART等,其最大频率通常为36MHz。
由于系统有多个USART,有些被连接到APB1,有些被连接到APB2,此处不矛盾。
void HAL_MspInit(void)
{/*设置APB2使能寄存器上,用于控制SYSCFG的时钟*/__HAL_RCC_SYSCFG_CLK_ENABLE();/*设置APB1使能寄存器上,用于控制PWR的时钟*/__HAL_RCC_PWR_CLK_ENABLE();
}
__IO
关键字是一个类型限定符,用于指定一个变量是可读写的(即双向的)。
它通常用于定义与外设寄存器相关的变量,这些变量需要通过内存映射的方式访问。
#define __HAL_RCC_SYSCFG_CLK_ENABLE() do { \__IO uint32_t tmpreg = 0x00U; \SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN);\/* Delay after an RCC peripheral clock enabling */ \tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN);\UNUSED(tmpreg); \} while(0U)
它在 RCC->APB2ENR 寄存器中设置 RCC_APB2ENR_SYSCFGEN 位,从而启用SYSCFG的时钟。APB2ENR 是高级外设总线2的使能寄存器。
#define __HAL_RCC_PWR_CLK_ENABLE() do { \__IO uint32_t tmpreg = 0x00U; \SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\/* Delay after an RCC peripheral clock enabling */ \tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\UNUSED(tmpreg); \} while(0U)
SystemClock_Config
系统时钟配置包括PWR使能、配置电压调节器等级、初始化SYSCLK、AHB、APB
AHB总线是一种高速总线,用于连接处理器核心、内存和高速外设。如内存和DMA控制器。
APB1/2总线相对AHB总线来讲,是一种低速总线,挂载常用外设。
系统时钟(SYSCLK)
AHB 总线时钟(HCLK)
APB1 总线时钟(PCLK1)
APB2 总线时钟(PCLK2)
系统时钟(SYSCLK)的来源可以是以下几种
高速内部振荡器(HSI)
高速外部晶振(HSE)
PLL(相位锁环)输出
系统时钟来源于高速外部/高速内部/锁相环,分频后得到AHB总线时钟,再分频得到APB1/2时钟。
SYSCLK (系统时钟)↓
HCLK (AHB 总线时钟) = SYSCLK / AHB 分频值↓
PCLK1 (APB1 总线时钟) = HCLK / APB1 分频值↓
PCLK2 (APB2 总线时钟) = HCLK / APB2 分频值
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};//用于配置时钟源的结构体RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};//用于配置CPU和时钟总线的结构体/*PWR使能*/__HAL_RCC_PWR_CLK_ENABLE(); //启用电源模块(PWR模块的时钟)__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); //配置微控制器的电压调节器输出电压。PWR_REGULATOR_VOLTAGE_SCALE1 表示选择电压调节器的最高电压等级,以支持更高的时钟频率。/*配置RCC时钟和PLL锁相环*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 144;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 3;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** 初始化SYSCLK、AHB和APB总线时钟 */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;/*!< 系统时钟(SYSCLK)来源:PLL 输出时钟。 */RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; /*!< AHB 总线时钟(HCLK)分频:不分频,与 SYSCLK 同频。 */RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; /*!< APB1 总线时钟(PCLK1)分频:HCLK 的一半。 */RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; /*!< APB2 总线时钟(PCLK2)分频:与 HCLK 同频。 */RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; //初始化时钟 并设置FLASH有2个延迟周期if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}
/*振荡器初始化结构体*/
typedef struct
{uint32_t OscillatorType; /*!< 振荡器类型:- 可选值:HSE、LSE、HSI、LSI。- 可组合配置。 */uint32_t HSEState; /*!< HSE 状态:- 可选值:OFF、ON、BYPASS。 *///BYPASS旁路模式(使用外部时钟信号)uint32_t LSEState; /*!< LSE 状态:- 可选值:OFF、ON、BYPASS。 */uint32_t HSIState; /*!< HSI 状态:- 可选值:OFF、ON。 */uint32_t HSICalibrationValue; /*!< HSI 校准值:- 范围:0x00~0x1F。- 默认值:16。 */uint32_t LSIState; /*!< LSI 状态:- 可选值:OFF、ON。 */RCC_PLLInitTypeDef PLL; /*!< PLL 配置:- 包含 PLL 分频参数。 */
} RCC_OscInitTypeDef; /*!< 振荡器和 PLL 初始化结构体。 */
/*时钟初始化结构体*/
typedef struct
{uint32_t ClockType; /*!< 要配置的时钟类型:- 可选值:系统时钟(SYSCLK)、AHB 时钟(HCLK)、APB1 时钟(PCLK1)、APB2 时钟(PCLK2)。 */uint32_t SYSCLKSource; /*!< 系统时钟(SYSCLK)的来源:- 可选值:HSI、HSE、PLL 等。 */uint32_t AHBCLKDivider; /*!< AHB 时钟(HCLK)的分频因子:- 由系统时钟(SYSCLK)分频得到。 */uint32_t APB1CLKDivider; /*!< APB1 时钟(PCLK1)的分频因子:- 由 AHB 时钟(HCLK)分频得到。 */uint32_t APB2CLKDivider; /*!< APB2 时钟(PCLK2)的分频因子:- 由 AHB 时钟(HCLK)分频得到。 */} RCC_ClkInitTypeDef; /*!< 时钟初始化结构体,用于配置系统时钟及相关分频器。 */
初始化配置外设
/*初始化用到的外设*/MX_GPIO_Init();MX_USART1_UART_Init();MX_USB_DEVICE_Init();
MX_GPIO_Init
使能GPIO时钟,
配置引脚的 模式、上下拉、速度。
设置外部中断EXTI0的优先级、使能外部中断。
void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0}; /*!< 初始化 GPIO 配置结构体 *//* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE(); /*!< 使能 GPIOC 时钟 */__HAL_RCC_GPIOH_CLK_ENABLE(); /*!< 使能 GPIOH 时钟 */__HAL_RCC_GPIOA_CLK_ENABLE(); /*!< 使能 GPIOA 时钟 *//* Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED_T_GPIO_Port, LED_T_Pin, GPIO_PIN_RESET); /*!< 设置 LED_T 引脚为低电平(熄灭) *//* Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = LED_T_Pin; /*!< 配置 LED_T 引脚 */GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /*!< 设置为推挽输出模式 */GPIO_InitStruct.Pull = GPIO_NOPULL; /*!< 禁用内部上下拉 */GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; /*!< 设置为低速 */HAL_GPIO_Init(LED_T_GPIO_Port, &GPIO_InitStruct); /*!< 初始化 LED_T 引脚 *//* Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = KEY1_Pin; /*!< 配置 KEY1 引脚 */GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; /*!< 设置为下降沿触发中断 */GPIO_InitStruct.Pull = GPIO_PULLUP; /*!< 启用内部上拉 */HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct); /*!< 初始化 KEY1 引脚 *//* EXTI interrupt init */HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); /*!< 设置 EXTI0 中断优先级 */HAL_NVIC_EnableIRQ(EXTI0_IRQn); /*!< 使能 EXTI0 中断 */
}
MX_USART1_UART_Init
void MX_USART1_UART_Init(void)
{huart1.Instance = USART1; // 选择 USART1 实例huart1.Init.BaudRate = 115200; // 波特率设置为 115200huart1.Init.WordLength = UART_WORDLENGTH_8B; // 数据位长度为 8 位huart1.Init.StopBits = UART_STOPBITS_1; // 1 个停止位huart1.Init.Parity = UART_PARITY_NONE; // 无校验位huart1.Init.Mode = UART_MODE_TX_RX; // 模式:发送和接收huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控制huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 过采样为 16if (HAL_UART_Init(&huart1) != HAL_OK) // 初始化 UART{Error_Handler(); // 初始化失败时调用错误处理函数}
}
MX_USB_DEVICE_Init
void MX_USB_DEVICE_Init(void)
{// 初始化 USB 设备库if (USBD_Init(&hUsbDeviceFS, /*USBD_HandleTypeDef hUsbDeviceFS */&FS_Desc, DEVICE_FS) != USBD_OK){Error_Handler(); // 初始化失败,调用错误处理函数}// 注册 USB 设备类(Mass Storage Class)if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC) != USBD_OK){Error_Handler(); // 注册类失败,调用错误处理函数}// 注册存储接口函数(用于 Mass Storage Class)if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK){Error_Handler(); // 注册存储接口失败,调用错误处理函数}// 启动 USB 设备库if (USBD_Start(&hUsbDeviceFS) != USBD_OK){Error_Handler(); // 启动失败,调用错误处理函数}
}
初始化句柄
/*USB初始化句柄*/
typedef struct _USBD_HandleTypeDef
{uint8_t id; // 设备实例 IDuint32_t dev_config; // 当前设备配置uint32_t dev_default_config; // 默认设备配置uint32_t dev_config_status; // 配置状态USBD_SpeedTypeDef dev_speed; // 设备速度(低速、全速、高速)USBD_EndpointTypeDef ep_in[16]; // IN 端点数组(最多 16 个)USBD_EndpointTypeDef ep_out[16]; // OUT 端点数组(最多 16 个)__IO uint32_t ep0_state; // 端点 0 的状态uint32_t ep0_data_len; // 端点 0 数据长度__IO uint8_t dev_state; // 设备状态__IO uint8_t dev_old_state; // 设备上一状态uint8_t dev_address; // 设备地址uint8_t dev_connection_status; // 连接状态uint8_t dev_test_mode; // 测试模式uint32_t dev_remote_wakeup; // 远程唤醒功能uint8_t ConfIdx; // 配置索引USBD_SetupReqTypedef request; // 设置请求结构体USBD_DescriptorsTypeDef *pDesc; // 指向描述符的指针USBD_ClassTypeDef *pClass[USBD_MAX_SUPPORTED_CLASS]; // 支持的类指针数组void *pClassData; // 类数据指针void *pClassDataCmsit[USBD_MAX_SUPPORTED_CLASS]; // CMSIT 类数据指针数组void *pUserData[USBD_MAX_SUPPORTED_CLASS]; // 用户数据指针数组void *pData; // 通用数据指针void *pBosDesc; // BOS 描述符指针void *pConfDesc; // 配置描述符指针uint32_t classId; // 类 IDuint32_t NumClasses; // 支持的类数量
#ifdef USE_USBD_COMPOSITEUSBD_CompositeElementTypeDef tclasslist[USBD_MAX_SUPPORTED_CLASS]; // 复合设备类列表
#endif /* USE_USBD_COMPOSITE */
} USBD_HandleTypeDef;
设备描述符
/*设备描述符*/
USBD_DescriptorsTypeDef FS_Desc =
{USBD_FS_DeviceDescriptor, // 设备描述符USBD_FS_LangIDStrDescriptor, // 语言 ID 字符串描述符USBD_FS_ManufacturerStrDescriptor,// 制造商字符串描述符USBD_FS_ProductStrDescriptor, // 产品字符串描述符USBD_FS_SerialStrDescriptor, // 序列号字符串描述符USBD_FS_ConfigStrDescriptor, // 配置字符串描述符USBD_FS_InterfaceStrDescriptor // 接口字符串描述符
#if (USBD_LPM_ENABLED == 1) // 如果启用了 LPM(低功耗模式), USBD_FS_USR_BOSDescriptor // BOS 描述符
#endif /* (USBD_LPM_ENABLED == 1) */
};
/* #define for FS and HS identification */
#define DEVICE_FS 0 // 定义全速设备标识
#define DEVICE_HS 1 // 定义高速设备标识
初始化USB设备
void MX_USB_DEVICE_Init(void)
{// 初始化 USB 设备库if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK){Error_Handler(); // 初始化失败,调用错误处理函数}// 注册 USB 设备类(Mass Storage Class)if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC) != USBD_OK){Error_Handler(); // 注册类失败,调用错误处理函数}// 注册存储接口函数(用于 Mass Storage Class)if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK){Error_Handler(); // 注册存储接口失败,调用错误处理函数}// 启动 USB 设备库if (USBD_Start(&hUsbDeviceFS) != USBD_OK){Error_Handler(); // 启动失败,调用错误处理函数}
}
USB_Init
主要作用是将设备描述符赋给初始化句柄。
然后调用USB设备的底层(Low Level)初始化 USBD_LL_Init。
USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev,USBD_DescriptorsTypeDef *pdesc, uint8_t id)
{USBD_StatusTypeDef ret;// 检查设备句柄是否有效if (pdev == NULL){
#if (USBD_DEBUG_LEVEL > 1U)USBD_ErrLog("Invalid Device handle"); // 调试日志:无效设备句柄
#endif /* (USBD_DEBUG_LEVEL > 1U) */return USBD_FAIL; // 返回失败}#ifdef USE_USBD_COMPOSITE// 如果启用复合设备支持,初始化类列表for (uint32_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++){pdev->pClass[i] = NULL; // 清空类指针pdev->pUserData[i] = NULL; // 清空用户数据指针pdev->tclasslist[i].Active = 0; // 设置类为非活动状态}pdev->NumClasses = 0; // 初始化类数量为 0pdev->classId = 0; // 初始化类 ID 为 0
#else// 如果未启用复合设备支持,仅初始化默认类pdev->pClass[0] = NULL; // 清空类指针pdev->pUserData[0] = NULL; // 清空用户数据指针
#endif /* USE_USBD_COMPOSITE */pdev->pConfDesc = NULL; // 清空配置描述符指针// 分配设备描述符if (pdesc != NULL){pdev->pDesc = pdesc; // 设置设备描述符}// 初始化设备状态pdev->dev_state = USBD_STATE_DEFAULT; // 设置为默认状态pdev->id = id; // 设置设备 ID// 初始化底层驱动ret = USBD_LL_Init(pdev); // 调用底层初始化函数return ret; // 返回初始化结果
}
USBD_LL_Init
-
检查设备 ID:确保设备为全速设备(
DEVICE_FS
)。 -
链接驱动与堆栈:将 USB 设备句柄与底层驱动句柄相互关联。
-
配置 USB OTG FS 实例:设置 USB OTG FS 的初始化参数,包括端点数量、速度、PHY 接口等。
-
初始化 USB OTG FS:调用 HAL 库的
HAL_PCD_Init
函数初始化 USB OTG FS。 -
注册回调函数(如果启用):注册 USB PCD 的各种回调函数,用于处理 USB 事件。
-
配置 FIFO:设置接收和发送 FIFO(缓冲区,单位字节) 的大小,用于数据传输。
这里将USB端点配置为 4,作用为
-
端点 0:控制端点(用于设备配置和控制请求)。
-
端点 1:IN 端点(用于从设备到主机的数据传输)。
-
端点 2:OUT 端点(用于从主机到设备的数据传输)。
-
端点 3:中断端点(用于设备状态的频繁更新,如键盘、鼠标)。
速度:决定了设备的数据传输速率,常见的有全速(12 Mbps)和高速(480 Mbps)。
PHY 接口:决定了设备的物理层接口类型,可以选择嵌入式 PHY 或外部 PHY。
嵌入式 PHY 是集成在微控制器芯片内部的物理层接口。
外部 PHY 是独立于微控制器芯片的物理层接口,通常以独立芯片的形式存在。
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{/* 初始化 USB IP */if (pdev->id == DEVICE_FS) {/* 将驱动与堆栈链接 */hpcd_USB_OTG_FS.pData = pdev;pdev->pData = &hpcd_USB_OTG_FS;/* 配置 USB OTG FS 实例 */hpcd_USB_OTG_FS.Instance = USB_OTG_FS;hpcd_USB_OTG_FS.Init.dev_endpoints = 4; // 配置设备端点数量hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL; // 设置为全速模式hpcd_USB_OTG_FS.Init.dma_enable = DISABLE; // 禁用 DMAhpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED; // 使用嵌入式 PHYhpcd_USB_OTG_FS.Init.Sof_enable = DISABLE; // 禁用 SOF 事件hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE; // 禁用低功耗模式hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE; // 禁用 LPMhpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE; // 禁用 VBUS 检测hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE; // 禁用专用端点 1/* 初始化 USB OTG FS */if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK){Error_Handler(); // 初始化失败,调用错误处理函数}#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)/* 注册 USB PCD 回调函数 */HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback);HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_FS, PCD_DataOutStageCallback);HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_FS, PCD_DataInStageCallback);HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOOUTIncompleteCallback);HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOINIncompleteCallback);
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS *//* 配置 USB FIFO */HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80); // 设置接收 FIFO 大小HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40); // 设置发送 FIFO 0 大小HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80); // 设置发送 FIFO 1 大小/*设备可以接收最多 128 字节的数据,存储在接收 FIFO 中。设备可以发送最多 64 字节的数据,存储在发送 FIFO 0 中。设备可以发送最多 128 字节的数据,存储在发送 FIFO 1 中。*/}return USBD_OK; // 返回初始化成功
}
USBD_RegisterClass
就是将设备类绑定到USB句柄
USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, //指向USB初始化句柄的指针USBD_ClassTypeDef *pclass)//指向USB设备类的指针
{uint16_t len = 0U;// 检查类指针是否为空if (pclass == NULL){
#if (USBD_DEBUG_LEVEL > 1U)USBD_ErrLog("Invalid Class handle");
#endif /* (USBD_DEBUG_LEVEL > 1U) */return USBD_FAIL;}// 将类绑定到USB设备句柄pdev->pClass[0] = pclass;// 获取设备配置描述符
#ifdef USE_USB_HSif (pdev->pClass[pdev->classId]->GetHSConfigDescriptor != NULL){pdev->pConfDesc = (void *)pdev->pClass[pdev->classId]->GetHSConfigDescriptor(&len);}
#else /* Default USE_USB_FS */if (pdev->pClass[pdev->classId]->GetFSConfigDescriptor != NULL){pdev->pConfDesc = (void *)pdev->pClass[pdev->classId]->GetFSConfigDescriptor(&len);}
#endif /* USE_USB_FS */// 类数量加1pdev->NumClasses ++;return USBD_OK;
}
/*设备类指针*/
typedef struct _Device_cb
{uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); // 初始化设备类uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); // 反初始化设备类uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); // 处理控制请求uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev); // EP0 发送完成回调uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev); // EP0 接收完成回调uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); // 数据 IN 端点回调uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); // 数据 OUT 端点回调uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev); // SOF 周期性回调uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); // Isochronous IN 未完成回调uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); // Isochronous OUT 未完成回调uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); // 获取高速配置描述符uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); // 获取全速配置描述符uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); // 获取其他速度配置描述符uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); // 获取设备限定符描述符
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); // 获取用户自定义字符串描述符
#endif /* USBD_SUPPORT_USER_STRING_DESC */
} USBD_ClassTypeDef;
USBD_MSC_RegisterStorage
将存储设备的操作接口绑定到USB句柄。
uint8_t USBD_MSC_RegisterStorage(USBD_HandleTypeDef *pdev, //USB句柄USBD_StorageTypeDef *fops) //存储接口
{// 检查存储操作接口指针是否为空if (fops == NULL){return (uint8_t)USBD_FAIL;}// 将存储操作接口绑定到设备句柄pdev->pUserData[pdev->classId] = fops;// 返回成功状态return (uint8_t)USBD_OK;
}
/*USB存储设备的操作接口*/
typedef struct _USBD_STORAGE
{int8_t (*Init)(uint8_t lun); // 初始化存储设备int8_t (*GetCapacity)(uint8_t lun, uint32_t *block_num, uint16_t *block_size); // 获取存储容量int8_t (*IsReady)(uint8_t lun); // 检查存储设备是否就绪int8_t (*IsWriteProtected)(uint8_t lun); // 检查存储设备是否写保护int8_t (*Read)(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); // 读取数据int8_t (*Write)(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); // 写入数据int8_t (*GetMaxLun)(void); // 获取最大逻辑单元号int8_t *pInquiry; // 存储设备的 Inquiry 数据
} USBD_StorageTypeDef;
USBD_Start
启动USB设备,底层大概就是根据 USB基地址进行数据读写和获取状态。
用户手册关于USB会有两块地址,
一块是USB寄存器的基地址,另一块是USB的SRAM地址(缓冲区)。
/*启动USB设备*/
USBD_StatusTypeDef USBD_Start(USBD_HandleTypeDef *pdev)
{
#ifdef USE_USBD_COMPOSITEpdev->classId = 0U; // 如果是复合设备,初始化类ID为0
#endif /* USE_USBD_COMPOSITE */// 启动底层驱动return USBD_LL_Start(pdev);
}
/*USB的底层启动程序*/
USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev)
{HAL_StatusTypeDef hal_status = HAL_OK; // HAL状态变量初始化为成功USBD_StatusTypeDef usb_status = USBD_OK; // USB状态变量初始化为成功// 启动HAL层的PCD(Peripheral Controller Driver)驱动hal_status = HAL_PCD_Start(pdev->pData);// 将HAL状态转换为USB状态usb_status = USBD_Get_USB_Status(hal_status);return usb_status; // 返回USB状态
}
/*Hal层的外设控制驱动*/
HAL_StatusTypeDef HAL_PCD_Start(PCD_HandleTypeDef *hpcd)
{USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; // 获取USB实例__HAL_LOCK(hpcd); // 加锁,防止多线程访问冲突// 如果启用了电池充电功能且不是ULPI PHY接口if ((hpcd->Init.battery_charging_enable == 1U) &&(hpcd->Init.phy_itface != USB_OTG_ULPI_PHY)){// 启用USB收发器USBx->GCCFG |= USB_OTG_GCCFG_PWRDWN;}__HAL_PCD_ENABLE(hpcd); // 启用PCD(Peripheral Controller Driver)(void)USB_DevConnect(hpcd->Instance); // 连接USB设备__HAL_UNLOCK(hpcd); // 解锁return HAL_OK; // 返回成功状态
}
循环部分
就是根据标志位翻转引脚和发串口。
中断部分
在xxx_it.c中,一个外部中断函数,一个
关于按键的中断,这里有一个中断嵌套的概念要提示。
高优先级中断打断低优先级中断,执行完再返回低优先级中断继续执行。
同等级优先级中断,比如中断A和中断B同优先级,则是先来的执行完了才处理后来的。
当外部中断被触发时(例如,按键按下导致 GPIO 状态变化),硬件会设置对应的中断标志位。对于外部中断,这些标志位通常存储在 EXTI(外部中断/事件控制器)的
PR
寄存器(中断挂起寄存器)中。
也就是一个按键多按几下,进不进外部中断要看PR寄存器清零的时机。
OTG_FS_IRQHandler
中断处理:处理 USB 设备模式下的各种中断事件,包括接收中断、发送中断、设备状态变化等。
状态更新:更新设备状态,如帧编号、中断标志等。
回调触发:根据中断类型触发相应的回调函数,通知上层应用处理数据传输或状态变化。
通过寄存器状态判断产生了哪种类型的中断,调用相应的处理函数。
void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
{// 确保设备处于设备模式if (USB_GetMode(hpcd->Instance) != USB_OTG_MODE_DEVICE) return;// 避免无效中断if (__HAL_PCD_IS_INVALID_INTERRUPT(hpcd)) return;// 更新帧编号hpcd->FrameNumber = (USBx_DEVICE->DSTS & USB_OTG_DSTS_FNSOF_Msk) >> USB_OTG_DSTS_FNSOF_Pos;// 处理接收队列中断if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL)){// 读取接收数据并更新缓冲区}// 处理输出端点中断if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_OEPINT)){// 遍历输出端点,处理数据传输完成、设置阶段完成等事件}// 处理输入端点中断if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_IEPINT)){// 遍历输入端点,处理数据传输完成、FIFO 空等事件}// 处理唤醒中断if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_WKUINT)){// 触发唤醒回调}// 处理挂起中断if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_USBSUSP)){// 触发挂起回调}// 处理 USB 复位if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_USBRST)){// 初始化设备状态,触发复位回调}// 处理枚举完成if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_ENUMDNE)){// 触发枚举完成回调}// 处理其他中断(如 SOF、LPM、连接/断开事件)
}
带参数的宏
基本用法
#define MACRO_NAME(parameter_list) macro_body
加法宏
#define ADD(x, y) ((x) + (y))
字符串化运算符 和 标记化运算符
#define STRINGIFY(x) #x
#define TOUPPER(x) STR##xint main() {char *s = STRINGIFY(Hello World); // "Hello World"char *t = TOUPPER(ing); // "STRING"return 0;
}