当前位置: 首页 > news >正文

炸鸡派-基础测试例程

基础例程

        代表非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配置

        预取指、指令缓存、数据缓存。

三个宏定义的标志位都是1U,无符号整数

        这里几个配置就是给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)地址区域。

这里的240代表240个可屏蔽中断

设置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
  1. 检查设备 ID:确保设备为全速设备(DEVICE_FS)。

  2. 链接驱动与堆栈:将 USB 设备句柄与底层驱动句柄相互关联。

  3. 配置 USB OTG FS 实例:设置 USB OTG FS 的初始化参数,包括端点数量速度PHY 接口等。

  4. 初始化 USB OTG FS:调用 HAL 库的 HAL_PCD_Init 函数初始化 USB OTG FS。

  5. 注册回调函数(如果启用):注册 USB PCD 的各种回调函数,用于处理 USB 事件。

  6. 配置 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;
}

http://www.lqws.cn/news/585631.html

相关文章:

  • AdGuard Home 安装及使用
  • 插入排序的简单介绍
  • 在 VS Code 中安装与配置 Gemini CLI 的完整指南
  • 第28篇:深入解析OpenEuler 24.03中的PAM认证机制:从原理到实践
  • 面向安全产品测试的静态混淆型 Shellcode Loader 设计与对抗分析
  • react经验:在nextjs中使用motion组件
  • 设计模式-访问者模式
  • PHP WebSocket服务器搭建指南
  • 深度学习03 人工神经网络ANN
  • 大数据(3)-Hive
  • iOS 应用上架踩坑实录:7 个问题 + 工具组合解决方案详解
  • STL简介+string模拟实现
  • 【Docker基础】Docker数据持久化与卷(Volume)介绍
  • 24V转12V降压实际输出12.11V可行性分析
  • gravitino0.9 安装部署集成Trino-439查询Hive3.1.3和MySQL8.0.33
  • 88.LMS当幅度和相位同时失配时,为啥最后权值w的相位angle(w(end))收敛到angle(mis)不是-angle(mis)
  • 从零到一通过Web技术开发一个五子棋
  • SpringBoot --项目启动的两种方式
  • js遍历对象的方法
  • 【MySQL】数据库基础
  • .net8导出影像图片按现场及天拆分
  • 51单片机CPU工作原理解析
  • 借助 KubeMQ 简化多 LLM 集成
  • YOLOv12_ultralytics-8.3.145_2025_5_27部分代码阅读笔记-torch_utils.py
  • 后台填坑记——Golang内存泄漏问题排查(一)
  • 设计模式(六)
  • 大模型开源技术解析 4.5 的系列开源技术解析:从模型矩阵到产业赋能的全栈突破
  • 2025年06月30日Github流行趋势
  • 遥控器双频无线模块技术要点概述
  • SegChange-R1:基于大型语言模型增强的遥感变化检测