1、STM32USB 使用方法要点使用方法要点 STM32-USB使用方法 开发板买的是奋斗 mini 开发板,芯片是:STM32F103VET6.如果需要源程序keil4-arm工程,上位机 vb。加 qq339396264 要程序。USB开发涉及主机和设备,为了避免开发驱动程序,使用 Windows自带的驱动程序。所以设备枚举成 HID 类设备。USB鼠标就是标准的 USB-HID 设备。不过操作系统阻止了应用程序直接访问 USB鼠标返回的报告。所以本例使用自定义 HID设备。一来免去了开发驱动程序,二来自定义的 HID设备应用程序和设备可以自由收发数据(仅指数据内容)。本文主要介绍 STM3
2、2 的 USB模块的简单使用,不会介绍 USB协议。主要是介绍一下 STM32F103 的 USB模块使用。USB模块从初始化首先是配置和使能时钟 下面是时钟的初始化:void Set_USBClock(void)/RCC_USBCLKSource_PLLCLK_1Div5 表示【USB时钟=PLL时钟除以 1.5】【72/1.5=48MHz】RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB,ENABLE);/【使能配置好了的 USB时钟】首先系统时钟要设置为 72
3、MHz,然后配置 USB时钟为 48MHz 并使能。然后是配置中断 void USB_Interrupts_Config(void)#define USB_LP_CAN1_RX0_IRQn 20 NVIC_InitTypeDef NVIC_InitStructure;/【使能 USB中断】NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;/【USB低优先级中断】NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;NVIC_InitStructure.NVIC_IRQChann
4、elSubPriority=0;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);然后初始化模块的一些寄存器 USART_send_str(USB 断开rn);USB_DISCONNECT/这是断开 1.5k 上拉电阻 delay(0 x7AFFFF);/延时一会 USB_CONNECT/使能 1.5K上拉电阻 USART_send_str(USB 连接rn);DADDR=0 x0080;/USB模块使能位。EF必须置 1 CNTR=0 x0001;/强制复位 CNTR=0 x0000;/退出
5、复位 ISTR=0 x0000;/清除可能产生的假中断 CNTR=(I_CTR|I_RESET);/使能一些中断,为了简单这里只使能了【总线复位中断】和【数据正确传输中断】ISTR=0 x0000;/清除可能产生的假中断 自此 USB模块初始化完毕。以后的流程就在 USB 中断的驱动下执行。USB中断函数:void USB_LP_CAN1_RX0_IRQHandler(void)u16 wIstr;/USB中断状态寄存器 wIstr=(u16)ISTR;/【USB复位中断】if(wIstr&I_RESET)USB_RST_Process();return;/【正确传输中断】if(wIstr&I
6、_CTR)USB_CTR_Process();return;在 USB中断函数中就是判断中断源,并转向相应的中断服务函数。USB总线复位后,USB相关的一些寄存器会复位。在 USB总线复位中要配置一下寄存器和数据收发的缓冲区。程序如下 void USB_RST_Process(void)/清除中断 ISTR=(u16)(I_RESET);/清除 ISTR 寄存器中断 RESET 位。写 0清除,写 1寄存器位内容不变。/USART_send_str(【USB】【复】【位】【中】【断】rn);usb_status=0;/复位处理 DADDR=0 x0080;/USB模块使能位。EF必须置 1 B
7、TABLE=0;/复位后默认为 0 EP0REG=(u16)(0 x0220);/0000 0010 0010 0000 端点 0初始化:控制端点,NAK主机的 IN令牌,端点号 0 /设置 TX/RX起始地址 USB_ADDR0_TX=32;/32-95 端点 0 的发送缓冲区从 32开始到 95,共 64字节 USB_ADDR0_RX=96;/96-159 端点 0 的接收缓冲区从 96开始到 159,共 64字节 /设置端点 0接收缓冲区大小:64Byte USB_CNT0_RX=0 x8400;/1000,0100,0000,0000 /【端点 1】EP1REG=(u16)(0 x06
8、21);/0000 0110 0010 0001 端点 0初始化:中断端点,NAK主机的 IN令牌,端点号 1 /设置 TX/RX起始地址 USB_ADDR1_TX=160;/160-223 端点 1的发送缓冲区从 160开始到 223,共 64字节 USB_ADDR1_RX=224;/224-287 端点 1 的接收缓冲区从 224开始到 287,共 64字节 /设置端点 1接收缓冲区大小:64Byte USB_CNT1_RX=0 x8400;/1000,0100,0000,0000 /设置 RX Valid.端点 0 可用于接收 u16 t=EP0REG;/读出端点 0寄存器值 t&=0
9、x8F8F;/1000 1111 1000 1111 由于 t 位写 0不影响原来的值,写 1 翻转,所以把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 由于 w0 位写 0清除,写 1 无影响,所以 w0位都设置为 1 t|=0 x3000;/0011 0000 0000 0000 把 SRAT_RX1:0设置为 1,通过写 1翻转来实现。EP0REG=t;下面是正确传输中断/USB分组正确传输处理 void USB_CTR_Process(void)/*【端点 0】*/if(ISTR&0 x0F)=0)/端点 0 if(ISTR&0 x10)=0)/
10、in 分组 /USART_send_str(*端点 0的 IN分组发送完成*rn);if(usb_status=1)usb_status=2;DADDR=0 x80|(usb_addr&0 xFF);/设置地址一定要找控制传输的状态完成后写入地址寄存器/USART_send_str(rn【写入地址完成】rn);/发送剩余数据 USB_ep0_send();/清除 CTR_TX u16 t=EP0REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 t&=0 xFF7
11、F;/1111 1111 0111 1111 把 CTR_TX 设置为 0,清除 CTR_RX 位 EP0REG=t;if(ISTR&0 x10)/【OUT】分组 or【SETUP】分组 u16 ep0r=EP0REG;/清除 CTR_RX u16 t=EP0REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 t&=0 x7FFF;/0111 1111 1111 1111 把 CTR_RX 设置为 0,清除 CTR_RX 位 EP0REG=t;if(ep0r&0
12、x0800)/SETUP 令牌包 /USART_send_str(端点 0收到【SETUP】分组rn);/默认控制端点处理枚举处理 ep0_setup_Process();memset(USB_rcv_buffer,0 x00,sizeof(USB_rcv_buffer);USB_enable_ep0_rx();else /USART_send_str(控制读传输:状态阶段rn);USB_read_EP0_buf(USB_rcv_buffer,(u32*)(PMAAddr+96*2);USB_enable_ep0_rx();/else/【IN分组传输成功】/*【端点 1】*/else if(I
13、STR&0 x0F)=1)USART_send_str(端点 1 数据完成传输rn);if(ISTR&0 x10)/【OUT分组】/清除 CTR_RX u16 t=EP1REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 t&=0 x7FFF;/0111 1111 1111 1111 把 CTR_RX 设置为 0,清除 CTR_RX位 EP1REG=t;USB_read_EP1_buf(USB_rcv_buffer,(u32*)(PMAAddr+224*2);/【
14、读 取】端点 1数据 /【处 理】端点 1 数据 USB_enable_ep1_rx();/【继续接受】端点 1数据 else/【IN分组】/清除 CTR_TX u16 t=EP1REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 t&=0 xFF7F;/1111 1111 0111 1111 把 CTR_TX 设置为 0,清除 CTR_RX 位 EP1REG=t;/标记发送完成 /*【端点 ERR】*/else USART_send_str(出错!端点号是没有开
15、启对端点。rn);其他代码:/设置 RX Valid.使端点继续接收 void USB_enable_ep0_rx(void)u16 t=EP0REG;/USART_send_str(EP0REG=);USART_send_HEX(t8);USART_send_HEX(t&0 xff);USART_send_str(rn);t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 switch(EP0REG&0 x3000)case 0 x0000:t|=0 x3000;bre
16、ak;case 0 x1000:t|=0 x2000;break;case 0 x2000:t|=0 x1000;break;case 0 x3000:t|=0 x0000;break;EP0REG=t;/t=EP0REG;/USART_send_str(EP0REG=);USART_send_HEX(t8);USART_send_HEX(t&0 xff);USART_send_str(rn);/设置 RX Valid.使端点继续接收 void USB_enable_ep1_rx(void)u16 t=EP1REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设
17、置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 switch(EP1REG&0 x3000)case 0 x0000:t|=0 x3000;break;case 0 x1000:t|=0 x2000;break;case 0 x2000:t|=0 x1000;break;case 0 x3000:t|=0 x0000;break;EP1REG=t;/读取端点 0收到的数据 u8 USB_read_EP0_buf(u8*buf,u32*USB_buf)u16 rcv_num;u8 read_cnt;u8 i;u16 temp;u8 j=0;/接收
18、到的数据 rcv_num=USB_CNT0_RX&0 x03FF;/unsigned char s32=0;/sprintf(char*)s,端点 0读取【%d】字节数据:,rcv_num);/USART_send_str(s);/USART_send_str(rn-rn);/读取次数 read_cnt=(rcv_num+1)1;for(i=0;i 8;/for(i=0;i1;for(i=0;i 8;for(i=0;ij;i+)USART_send_HEX(bufi);if(i+1)%16=0)USART_send_str(rn);USART_send_str(rn-rnrn);return(
19、j);/通过端点 0发送数据 void USB_write_EP0_buf(u8*buf,u32*USB_buf,u8 len)u8 send_cnt;u8 i;u8 j=0;u16 h,l;/unsigned char s32=0;/sprintf(char*)s,端点 0发送【%d】字节数据:,len);/USART_send_str(s);/USART_send_str(rn+rn);/for(i=0;i 1;for(i=0;isend_cnt;i+)l=bufj+;h=bufj+8;*USB_buf=(h|l);USB_buf+;/要发送数据的长度 USB_CNT0_TX=len;/使
20、能发送:设置 VALID u16 t=EP0REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0位都设置为 1 switch(EP0REG&0 x0030)case 0 x0000:t|=0 x0030;break;case 0 x0010:t|=0 x0020;break;case 0 x0020:t|=0 x0010;break;case 0 x0030:t|=0 x0000;break;EP0REG=t;/通过端点 1发送数据 void USB_write_EP1_buf(
21、u8*buf,u32*USB_buf,u8 len)u8 send_cnt;u8 i;u8 j=0;u16 h,l;unsigned char s32=0;sprintf(char*)s,端点 1发送【%d】字节数据:,len);USART_send_str(s);USART_send_str(rn+rn);for(i=0;i 1;for(i=0;isend_cnt;i+)l=bufj+;h=bufj+dev_d7)/如果需要发送数据长度大于端点 0 的最大长度:分包发送 USB_write_EP0_buf(p_send_ep0,(u32*)(PMAAddr+32*2),dev_d7);sen
22、d_len_ep0-=dev_d7;p_send_ep0+=dev_d7;else/需要发送的数据长度小于等于端点 0的最大包长 if(send_len_ep0!=0)/一次能够发送完成 USB_write_EP0_buf(p_send_ep0,(u32*)(PMAAddr+32*2),send_len_ep0);send_len_ep0=0;else/等于 0 if(need_0_pack_ep0=1)need_0_pack_ep0=0;USB_write_EP0_buf(p_send_ep0,(u32*)(PMAAddr+32*2),0);/端点 0枚举过程处理 void ep0_setu
23、p_Process(void)/读取接收到的数据 USB_read_EP0_buf(USB_rcv_buffer,(u32*)(PMAAddr+96*2);req_type=USB_rcv_buffer0;req=USB_rcv_buffer1;w_value=(USB_rcv_buffer38)|USB_rcv_buffer2;w_index=(USB_rcv_buffer58)|USB_rcv_buffer4;w_length=(USB_rcv_buffer75)&0 x03)/请求的类型 case 0:/标准请求.省略了对“请求的接收者”的判断 /USART_send_str(USB 标
24、准输入请求:);switch(req)/标准请求代码 case 0:/获取状态 /USART_send_str(获取状态rn);break;case 6:/获取描述符 /USART_send_str(获取描述符);switch(w_value8)&0 xFF)/描述符类型 case 1:/设备描述符 /USART_send_str(设备描述符rn);p_send_ep0=(u8*)dev_d;/设备描述符数组 if(w_length dev_d0)/如果请求的数据大于描述符长度,返回描述符长度最大长度 send_len_ep0=dev_d0;/18 if(send_len_ep0%dev_d7
25、)=0)need_0_pack_ep0=1;/是包长的整数倍,需要发送 0长度数据包 else/请求长度小于等于描述符长度,则返回请求长度 send_len_ep0=w_length;USB_ep0_send();break;case 2:/配置描述符 /USART_send_str(配置描述符rn);p_send_ep0=(u8*)cfg_d;/设备描述符数组 send_len_ep0=(cfg_d3 send_len_ep0)/如果请求的数据大于端点 0,返回端点 0最大长度 if(send_len_ep0%dev_d7)=0)need_0_pack_ep0=1;/是包长的整数倍,需要发送
26、 0长度数据包 else/请求小于等于端点 0,返回请求长度 send_len_ep0=w_length;USB_ep0_send();break;case 3:/字符串描述符 /USART_send_str(字符串描述符rn);break;case 0 x22:/报告描述符 /USART_send_str(报告描述符rn);p_send_ep0=(u8*)rpt_d;send_len_ep0=sizeof(rpt_d);if(w_length send_len_ep0)/如果请求的长度大于实际长度 if(send_len_ep0%dev_d7)=0)need_0_pack_ep0=1;/是包
27、长的整数倍,需要发送 0长度数据包 else send_len_ep0=w_length;/返回长度等于请求长度 USB_ep0_send();break;default:/其他描述符 /USART_send_str(其他描述符:);/USART_send_HEX(w_value8)&0 xFF);/USART_send_str(rn);break;break;case 8:/获取配置 /USART_send_str(获取配置rn);break;case 10:/获取接口 /USART_send_str(获取接口rn);break;case 12:/帧同步 /USART_send_str(帧同
28、步rn);break;default:/USART_send_str(USB未定义输入请求rn);break;break;case 1:/类请求 /USART_send_str(USB 类输入请求rn);break;case 2:/厂商请求 /USART_send_str(厂商输入请求rn);break;case 3:/保留 /USART_send_str(USB 输入请求:保留rn);break;else/*输出请求*/switch(req_type5)&0 x03)/请求的类型 case 0:/标准请求.省略了对“请求的接收者”的判断 /USART_send_str(USB 标准输出请求:
29、);switch(req)/标准请求代码【GET】case 1:/清除特性 /USART_send_str(清除特性rn);send_len_ep0=0;need_0_pack_ep0=1;USB_ep0_send();/发送数据 break;case 5:/设置地址 USART_send_str(设置地址rn);/USART_send_HEX(w_value&0 xFF);/USART_send_str(rn);usb_status=1;/表示主机设置了地址 usb_addr=w_value&0 xFF;send_len_ep0=0;need_0_pack_ep0=1;USB_ep0_sen
30、d();/发送数据 break;case 9:/设置配置 USART_send_str(设置配置rn);/设置 RX Valid.【端点 1】可用于接收 u16 t=EP1REG;t&=0 x8F8F;/1000 1111 1000 1111 把 t 位都设置为 0 t|=0 x8080;/1000 0000 1000 0000 把 w0 位都设置为 1 t|=0 x3000;/0011 0000 0000 0000 把 SRAT_RX1:0设置为 1 EP1REG=t;send_len_ep0=0;need_0_pack_ep0=1;USB_ep0_send();/发送剩余数据 usb_st
31、atus=3;break;case 7:/设置描述符 /USART_send_str(设置描述符rn);break;case 3:/设置特性 /USART_send_str(设置特性rn);break;case 11:/设置接口 /USART_send_str(设置接口rn);break;default:/出错 /USART_send_str(标准输出请求代码出错rn);break;break;case 1:/类请求 /USART_send_str(USB 类输出请求:);switch(req)case 0 x0A:USART_send_str(设置空闲rn);send_len_ep0=0;n
32、eed_0_pack_ep0=1;USB_ep0_send();/发送剩余数据 break;default:#ifdef HEEY_DEBUG /UART_send_str(未知请求rn);#endif break;break;case 2:/厂商请求 /USART_send_str(厂商输出请求rn);break;case 3:/保留 /USART_send_str(输出请求:保留rn);break;/描述符的话用数组实现的这里就不给出来。源程序的 kile 4-arm 写的,可以向我要。奋斗 STM32 迷你板应该下载后可以直接使用。上位机软件使用 vb。一下是枚举过程 USB断开 USB
33、连接【U】【S】【B】【复】【位】【中】【断】【U】【S】【B】【复】【位】【中】【断】端点 0【SETUP】分组接收完成 端点 0读取【8】字节数据:-0 x80 0 x06 0 x00 0 x01 0 x00 0 x00 0 x40 0 x00 -USB标准输入请求:获取描述符设备描述符 端点 0发送【18】字节数据:+0 x12 0 x01 0 x10 0 x01 0 x00 0 x00 0 x00 0 x40 0 xAA 0 xAA 0 x02 0 x00 0 x00 0 x01 0 x00 0 x00 0 x00 0 x01 +0 x62 0 x60 tx=2 tx2=3 rx=2 rx2=3*端点 0的 IN分组发送完成*rx=0 rx2=0 端点 0的【OUT】分组接收完成 端点 0读取【0】字节数据:-rx=0 rx2=0【U】【S】【B】【复】【位】【中】【断】端点 0【