在许多的PC的USB应用中,越来越多的需要使用低功耗功能,PC可结合USB提供一定的供电能力,使得外设在1.5W以内,可以不用其他单独的外部供电;更巧妙的是PC可以进入Sleep,驱使USB外设也进入Suspend状态,达到只提供2.5mA的供电需求,以达到低功耗的目的。灵动微电子推出的MM32F0270系列,支持多种灵活的低功耗模式,支持Suspend模式的USB Device模块。USB外设支持USB挂起/恢复操作,可以通过停止MCU时钟来实现降低功耗。
当USB设备处于挂起状态时,它仍然为其D+或D-及上拉电阻器供电,以保持空闲状态并保持其内部状态,包括地址和配置。当它被USB总线上的恢复信号唤醒时,不需要经历重新枚举过程。
USB总线上的恢复信号可以由主机和设备发送。远程唤醒功能使USB设备能够唤醒挂起的主机;例如,将鼠标连接到笔记本电脑时,您可以通过单击鼠标来唤醒已经进入睡眠状态的笔记本电脑(PC端需要配置为支持远程唤醒)。远程唤醒功能在枚举阶段在配置描述符中报告,并且可以使用标准 USB 请求启用(或禁用)。
本文介绍了如何使用 MM32F0270的USB来实现通过接收USB的D+/D-信号在实现Suspend/Resume的状态转换。
1. MM32F0270 USB的简要介绍
符合 USB 2.0全速设备的技术规范;
- 支持全速模式(12Mbps)
- 包含四个独立的通用端点和一个控制传输端点用于中断传输和批量传输
- 控制、批量 、 中断传输 最大可传输 64字节的包
- CRC生成 /校验, NRZI编码 /解码和位填充
- 支持 USB挂起 /恢复操作
- 支持 DMA传输
图1 USB的功能框图
2. USB的功能特性
2.1 MM32的USB具有以下特性:
USB模块可以为PC主机和微控制器提供一种符合USB规范的通信连接,用于两者所实现的功能之间的连接。PC主机和微控制器之间通过数据缓冲区完成数据传输,USB模块与PC主机通信,根据USB规范完成对令牌分组的检测,对数据发送/接收和握手分组的处理。由硬件完成对包括CRC的生成和校验等整个传输的格式。每一个端点都有一个64字节缓冲区描述块,且该缓冲区是在USB模块内部,不能直接被CPU访问的。一个有效的功能/端点的令牌分组被USB模块识别,当端点已配置完成后发生相关的数据传输。USB模块与专用数据缓冲区的数据交换通过内部寄存器完成。在所有的数据传输完成后,根据需要的传输方向,发送或接收适当的握手分组。
数据传输完成时,USB模块会触发端点相关的中断,通过读取状态寄存器或利用不同的中断处理程序,可以确定:
- 主机请求的传输类型
- 哪个端点需要得到服务
- 正在进行的是哪种类型的服务
- 端点的应答
- 传输是否完成
USB模块在不工作时,可以通过配置USB\_POWER寄存器使USB模块处于SUSPEND模式(低功耗模式)。USB模块在低功耗模式下,USB时钟会减慢或停止且USB模块不产生静态电流功耗。USB模块在低功耗模式下,可以通过USB线上数据传输将其唤醒,也可以通过软件直接将其唤醒。或者通过将特定的中断输入源连接到唤醒引脚上,使系统立即恢复正常的时钟,可以直接启动或停止时钟系统。
2.2 MM32F0270 USB用于挂起和恢复的USB总线状态
USB规范定义了与USB总线上的信号电压相对应的总线状态。
下图为连接到D+的1.5 kΩ上拉电阻的全速总线。
MM32F0270系列芯片内置1.5K上拉电阻。
下面介绍USB总线如何定义挂起、恢复和空闲状态的总线状态。
设备的挂起
在USB系统中,正常状态下PC, Hub或Root Hub会一直周期性地发送SOF包(Start Of Frame,全速USB每1ms发送一个,高速USB则是125µs发送一个)。根据USB协议,如果USB线上一直处于空闲(Idle)状态超过3ms,设备应该把它当作一个挂起(Suspended)信号,要求设备在10ms内进入挂起状态,并把设备所需的电流大小降到规定的值;对于low-power设备,要求是500µA,而对于high-power或支持远程唤醒(remote wakeup)功能的设备是2.5mA。在挂起状态中,设备必须继续向数据项D+/D-的上拉电阻提供电压以维持Idle状态。
设备的唤醒
设备处于挂起状态时,任何总线上的活动(非空闲信号)都可以把设备唤醒/恢复,从而退出低功耗模式。同样,设备也可以换醒host,比如电脑待机时通过USB键盘来换醒主机,这种功能称之为“远程唤醒”(remote wakeup),不在本文的讨论范围内。
因为设备挂起时处于全速信号,在当host需要把将设备退出suspend状态时,需要先发送一个持续时间超过20ms的Fulll Speed K状态。设备看到K状态结束的1.3us内醒过来,而host需要在3ms内发送SOF信号以维持正常的高速信号模式,否则设备又将进入suspend。如下图所示:
下面是USB的数据状态与信号的说明:
数据 J 和 K 状态Data J and K states
对于全速总线段,J 状态与差分 1 相同,即 D+ 为逻辑高电平时和 D- 为逻辑低电平,而 K 状态与差分 0 相同,即当 D+ 为逻辑低电平且D- 是逻辑高电平
空闲状态Idle state
在空闲状态下,对于全速总线段,D+ 比 D- 的电平高。
挂起状态Suspend signal
由于当USB总线处于空闲状态超过3 ms时进入挂起状态,因此挂起状态与空闲状态相同或与全速总线段中的J状态相同。
恢复信号Resume signal
当设备处于挂起状态时,设备端口上的数据K状态表示从挂起状态恢复。这意味着恢复信号是全速段中从数据J状态到数据K状态的变化。
2.3 MM32F0270 USB支持从Suspend模式下唤醒
- MM32F0270 可通过EXTI线 18连接到 USB总线挂起中断实现USB从挂起状态唤醒。
需要使用唤醒时,需要使能相应的USB中断外,还需配置EXTI
18以使能相关的功能。
相关的寄存器与控制状态位的控制与查询,可以参考MM32F0270的用户手册。
3. USB 进入Suspend与Resume的软硬件设计
3.1在库函数版本的样例中可以通过如下顺序初始化USB
a. 配置系统时钟为48MHz或96MHz,使能GPIOA时钟,使能USB时钟
void USB_ClockConfig(void)
{
USB_HSI48M_Config();
Set_CRS(); // The calibration of vibration
// Select USBCLK source
// RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div2); //if SYSCLK is 96MHz
// Enable USB clock
RCC_APB1PeriphClockCmd(RCC_APB1ENR_USB, ENABLE);
}
b. 配置USB D+和D-所需用到的GPIO引脚,使用GPIO\_Configuration函数
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
// USB_DISCONNECT used as USB pull-up
GPIO_InitStruct.GPIO_Pin = USB_DISCONNECT_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_FLOATING;
GPIO_Init(USB_DISCONNECT, &GPIO_InitStruct);
}
c. 配置USB的中断,调用USB\_NVIC\_Config函数
void USB_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USB_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
d. 设置USB的时钟,调用USB\_ClockConfig
void USB_ClockConfig(void)
{
USB_HSI48M_Config();
Set_CRS(); // The calibration of vibration
// Select USBCLK source
// RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div2);
// Enable USB clock
RCC_APB1PeriphClockCmd(RCC_APB1ENR_USB, ENABLE);
}
e. 设置USB 初始化
void USB_Init(void)
{
pInformation = &Device_Info;
pInformation->ControlState = 2;
pProperty = &Device_Property;
pUser_Standard_Requests = &User_Standard_Requests;
// Initialize devices one by one
pProperty->Init();
}
f. 设定USB对应唤醒的EXTI参数
void USB1_WKUP_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line18;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
g. 中断相应处理函数
void USB_Istr(void)
{
__IO u16 wIstr;
wIstr = _GetUSB_INT_STA();
//USB->INT_STATE = wIstr;
// fSuspendEnabled = 0;
if(wIstr & USB_INT_STATE_RSTF) {
_ClrUSB_INT_STA(USB_INT_STATE_RSTF) ;
Device_Property.Reset();
}
//…
EXTI_ClearITPendingBit(EXTI_Line22);
}
3.2 实现main Demo功能的主要函数代码
s32 main(void)
{
GPIO_Configuration();
USB_NVIC_Config();
USB_ClockConfig();
USB_Init();
while(1) {
if(bDeviceState == CONFIGURED) {
if(!(_GetUSB_CTRL1()&EP1_CTRL_TRANEN)) {
UserToPMABufferCopy(gTableData, ENDP1, BUFF_SIZE);
_SetUSB_CTRL1(EP1_CTRL_TRANEN | BUFF_SIZE) ; //Loop IN transmission
}
}
}
}
配置好初始化USB收发的初始化操作后,PC端会发现USB HID枚举成功;此时PC端进入待机模式。
MCU的USB收到Suspend的命令后,执行:
void Suspend(void)
{
if(bDeviceState == CONFIGURED) {
//Frequency the bus clock 512
USB->POWER &= ~USB_POWER_SUSP;
bDeviceState = SUSPENDED;
fSuspendEnabled = true;
RCC->CFGR &= ~RCC_CFGR_HPRE;
RCC->CFGR |= RCC_CFGR_HPRE_DIV512;
RCC->CR &= ~(0x1f << 26);
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div4);
UART_DeInit(UART1);
}
}
USB唤醒机制
USB设备进入挂起状态之后,将由Resume信号进行唤醒。Resume信号可以由USB主机发起,也可以由USB设备本身触发,但是只有USB主机可以结束Resume信号。
主机在挂起设备后可通过翻转数据线上的极性并保持20ms来唤醒设备,并以低速EOP信号结尾。
如果设备支持远程唤醒,设备可向主机发起远程唤醒请求,前提是设备已进入idle状态至少5ms,设备会驱动总线进入K状态,如下图,K状态必须维持1ms-15ms之内,此信号会在1ms内被主机接管,主机会继续驱动唤醒信号直到20ms,并以低速EOP信号结尾。
主机复位设备或者设备对自己强行复位,设备也会从挂起状态切换到默认状态。
MM32F0270 设备被唤醒后,如果唤醒中断使能(比如使用PA0的EXTI0),则会进入唤醒中断,退出低功耗模式,然后清除USB\\\_POWER寄存器的SUSPEND位,退出强制挂起操作。
操作上体现为PC Host主机同按主机KeyBoard或鼠标按键实现退出睡眠状态,从而唤醒MM32F0270的挂起状态。
通过上述的步骤,简单的演示了MM32F0270的USB 接收数据,通过数据帧中的Start Bit唤醒MCU的功能。
Demo程序可登录MindMotion的官网(https://www.mindmotion.com.cn/products/mm32mcu/mm32f/mm32f\_mainstream/MM32F0270/ )下载MM32F0270
lib\_Samples,工程路径如下:~ MM32F0270\_Samples\LibSamples\USB\。