一、前言
目的:使用 STM32F103 从 0 开始实现 USB 调试代码。
硬件:正点原子战舰开发板。
软件:
-
USB:用于实现各种功能 -
串口:打印日志信息 -
LED:显示运行状态 -
其他:根据需要进行添加
参考:STM 官方 USB 库。
二、工程目录说明
三、常用驱动说明
1、时钟配置
2、数据结构设计
1、循环队列
1、数据类型
#define QUEUE_MAXSIZE 128
typedef struct{
unsigned char data[QUEUE_MAXSIZE];
unsigned int front,rear;
}queue_t;
2、相关操作
#include "queue.h"
int init_queue(queue_t *q)
{
q->front = q->rear = 0;
return 0;
}
int en_queue(queue_t *q,unsigned char c)
{
if((q->rear+1)%QUEUE_MAXSIZE == q->front) return 1;
q->data[q->rear] = c;
q->rear = (q->rear+1)%QUEUE_MAXSIZE;
return 0;
}
int de_queue(queue_t *q,unsigned char *c)
{
if(q->rear == q->front) return 1;
*c = q->data[q->front];
q->front = (q->front+1)%QUEUE_MAXSIZE;
return 0;
}
3、UART
1、缓存设计
1、发送数据
发送数据不进行设计,根据需求进行。
2、接收数据
接收数据采用循环队列完成。
typedef struct{
queue_t data;
}uart_t;
2、初始化
int uart_init(unsigned int rate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = rate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
init_queue(&uart_data.data);
return 0;
}
3、发送数据
uart 作为日志数据发送,需要兼容不同格式类型数据,因此发送数据通过重定向 printf 完成。
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR = (u8) ch;
return ch;
}
#endif
4、接收数据
uart 接收数据为了能够及时响应,因此使用中断来完成。
int uart_recive_data(unsigned char c)
{
en_queue(&uart_data.data,c);
return 0;
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
uart_recive_data(USART_ReceiveData(USART1));
}
}
3、LED
int led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
return 0;
}
void led0_on(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}
void led0_off(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);
}
void led1_on(void)
{
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
}
void led1_off(void)
{
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
4、delay
延时使用 Systick 定时器完成,设计思想如下:
1、将 Systick 配置成每 1ms 中断一次。
2、根据延时时间设置中断次数。
代码如下:
static __IO unsigned int timing_delay;
int delay_init(void)
{
if(SysTick_Config(SystemCoreClock / 1000)){
printf("delay_init failed\r\n");
}
return 0;
}
void delay_ms(__IO unsigned int nTime)
{
timing_delay = nTime;
while(timing_delay != 0);
}
void timing_delay_decrement(void)
{
if (timing_delay != 0x00)
{
timing_delay--;
}
}
void SysTick_Handler(void)
{
timing_delay_decrement();
}
四、usb 驱动
说明:参考 STM 提供 USB 库。
1、GPIO 配置
static int usb_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
return 0;
}
2、时钟配置
static int usb_clock_init(void)
{
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
return 0;
}
3、中断配置
static int usb_interrupts_config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
特别说明:中断优先级等级根据需要进行调整。 中断处理函数如下:
int usb_istr(void)
{
unsigned int r_istr = 0;
r_istr = _GetISTR();
if(r_istr & ISTR_SOF){
_SetISTR((uint16_t)CLR_SOF);
printf("ISTR_SOF IRQHandler\r\n");
}
if(r_istr & ISTR_CTR){
printf("ISTR_CTR IRQHandler\r\n");
}
if(r_istr & ISTR_RESET){
_SetISTR((uint16_t)CLR_RESET);
printf("ISTR_RESET IRQHandler\r\n");
}
if(r_istr & ISTR_DOVR){
_SetISTR((uint16_t)CLR_DOVR);
printf("ISTR_DOVR IRQHandler\r\n");
}
if(r_istr & ISTR_ERR){
_SetISTR((uint16_t)CLR_ERR);
printf("ISTR_ERR IRQHandler\r\n");
}
if(r_istr & ISTR_WKUP){
_SetISTR((uint16_t)CLR_WKUP);
printf("ISTR_WKUP IRQHandler\r\n");
}
if(r_istr & ISTR_SUSP){
_SetISTR((uint16_t)CLR_SUSP);
printf("ISTR_SUSP IRQHandler\r\n");
}
if(r_istr & ISTR_ESOF){
_SetISTR((uint16_t)CLR_ESOF);
printf("ISTR_ESOF IRQHandler\r\n");
}
return 0;
}
void USB_LP_CAN1_RX0_IRQHandler(void)
{
usb_istr();
printf("USB_LP_CAN1_RX0_IRQHandler\r\n");
}
void USB_HP_CAN1_TX_IRQHandler(void)
{
printf("USB_HP_CAN1_TX_IRQHandler\r\n");
}
void USBWakeUp_IRQHandler(void)
{
printf("USBWakeUp_IRQHandler\r\n");
}
4、usb外设配置
int usb_register_init(void)
{
_SetCNTR(CNTR_FRES);
_SetCNTR(0);
while((_GetISTR()&ISTR_RESET) == 1);
_SetISTR(0);
_SetCNTR(CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM);
_SetISTR(0);
_SetCNTR(CNTR_CTRM|CNTR_WKUPM|CNTR_SUSPM|CNTR_ERRM|CNTR_SOFM|CNTR_ESOFM|CNTR_RESETM);
return 0;
}
五、工程模板测试
1、串口打印日志如下
[16:49:54.963] uart init successful!
[16:49:54.963] ISTR_ESOF IRQHandler
[16:49:54.963] USB_LP_CAN1_RX0_IRQHandler
[16:49:54.963] ISTR_SUSP IRQHandler
[16:49:54.963] ISTR_ESOF IRQHandler
……
[16:49:55.052] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.055] ISTR_RESET IRQHandler
[16:49:55.101] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.113] ISTR_SOF IRQHandler
[16:49:55.113] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.113] ISTR_SOF IRQHandler
[16:49:55.113] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.113] ISTR_SOF IRQHandler
[16:49:55.122] USB_LP_CAN1_RX0_IRQHandler
……
[16:49:57.084] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.084] ISTR_SOF IRQHandler
[16:49:57.084] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.084] ISTR_SOF IRQHandler
[16:49:57.092] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.092] ISTR_SOF IRQHandler
[16:49:57.092] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.092] ISTR_SOF IRQHandler
[16:49:57.102] ISTR_ESOF IRQHandler
[16:49:57.102] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.102] ISTR_SUSP IRQHandler
[16:49:57.102] ISTR_ESOF IRQHandler
[16:49:57.111] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.111] ISTR_SUSP IRQHandler
[16:49:57.111] ISTR_ESOF IRQHandler
[16:49:57.111] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.123] ISTR_SUSP IRQHandler
[16:49:57.123] ISTR_ESOF IRQHandler
[16:49:57.123] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.123] ISTR_SUSP IRQHandler
[16:49:57.123] ISTR_ESOF IRQHandler
[16:49:57.132] USB_LP_CAN1_RX0_IRQHandler
……
-
SOF:USB 模块检测到总线上的 SOF 分组时由硬件置位,标志一个新的 USB 帧的开始。中断服务程序可以通过检测 SOF 事件来完成与主机的 1ms 同步,并正确读出寄存器在收到 SOF 分组时 的更新内容(此功能在同步传输时非常有意义)。 -
ESOF:USB 模块未收到期望的 SOF 分组时由硬件置位。主机应该每毫秒都发送 SOF 分组,但如果 USB 模块没有收到,挂起定时器将触发此中断。如果连续发生 3 次 ESOF 中断也就是连续 3 次未收到 SOF 分组,将产生 SUSP 中断。 -
SUSP:USB 线上超过 3ms 没有信号传输时由硬件置位,用以指示一个来自 USB 总线的挂起请求。USB 复位后硬件立即使能对挂起信号的检测,但在挂起模式下(FSUSP=1)硬件不会再检 测挂起信号直到唤醒过程结束。
2、PC 枚举 usb 模块
|