// USB device实验程序解析
//头文件
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "driverlib/rom.h"
#include "grlib/grlib.h"
#include "usblib/usblib.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdbulk.h"
#include "utils/uartstdio.h"
#include "utils/ustdlib.h"
#include "drivers/cfal96x64x16.h"
#include "usb_bulk_structs.h"
// 系统节拍定时systick宏定义
// SYSTICKS_PER_SECOND 每秒中断次数
#define SYSTICKS_PER_SECOND 100
#define SYSTICK_PERIOD_MS (1000 / SYSTICKS_PER_SECOND)
// 全局systick计数
volatile ui32 g_ulSysTickCount = 0;
//全局变量记录发送和接收字节数
volatile ui32 g_ulTxCount = 0;
volatile ui32 g_ulRxCount = 0;
#ifdef DEBUG
ui32 g_ulUARTRxErrors = 0;
#endif
// 与debug相关的定义和声明
// 如果在编译时定义了DEBUG那么通过UART0进行Debug的输出
#ifdef DEBUG
//将所有Debug打印请求映射到UARTprintf输出
#define DEBUG_PRINT UARTprintf
#else
// 编译所有的Debug打印请求
#define DEBUG_PRINT while(0) ((int (*)(char *, ...))0)
#endif
// 图形上下文(context)用于OLED屏幕显示
tContext g_sContext;
// 宏定义标志,用于在中断时向主程序发送命令.
#define COMMAND_PACKET_RECEIVED 0x00000001
#define COMMAND_STATUS_UPDATE 0x00000002
volatile ui32 g_ulFlags = 0;
char *g_pcStatus;
// 全局标志显示USB设置是否完成
static volatile bool g_bUSBConfigured = false;
// 错误处理
#ifdef DEBUG
void
__error__(char *pcFilename, ui32 ulLine)
{
UARTprintf("Error at line %d of %s\n", ulLine, pcFilename);
while(1)
{
}
}
#endif
// systick中断处理
void
SysTickIntHandler(void)
{
// 更新tick计数
g_ulSysTickCount++;
}
//函数static ui32
// 功能:device接收数据并返回给主机。
// 当从主机数据发送就绪后,该程序被调用,逐个字节读取数据,并且翻转
//大小写,最后回传给主机
// 变量 psDevice 指向要处理的设备数据实例
// 变量 pcData 指向USB接收缓冲去新接收到的数据
// 变量ulNumBytes 是程序要处理的字节数。
// 返回:处理的数据字节数
static ui32
EchoNewDataToHost(tUSBDBulkDevice *psDevice, ui8 *pcData,
ui32 ulNumBytes)
{
ui32 ulLoop, ulSpace, ulCount;
ui32 ulReadIndex;
ui32 ulWriteIndex;
tUSBRingBufObject sTxRing;
// 获取当前缓冲区信息以允许设备能直接写入发送缓冲区。
//(变量中已经有足够信息与接收缓冲区直接连接)
USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
// 获取发送缓冲区的剩余空间
ulSpace = USBBufferSpaceAvailable(&g_sTxBuffer);
// 判断此次可以处理的字节数
ulLoop = (ulSpace < ulNumBytes) ? ulSpace : ulNumBytes;
ulCount = ulLoop;
// 更新接收字节数
g_ulRxCount += ulNumBytes;
// 显示Debug信息
DEBUG_PRINT("Received %d bytes\n", ulNumBytes);
// 与USB缓冲区连接启动字节处理
ulReadIndex = (ui32)(pcData - g_pucUSBRxBuffer);
ulWriteIndex = sTxRing.ui32WriteIndex;
while(ulLoop)
{ // 将接收缓冲去的字节拷入发送缓冲区
// 判断是否是小写字母
if((g_pucUSBRxBuffer[ulReadIndex] >= 'a') &&
(g_pucUSBRxBuffer[ulReadIndex] <= 'z'))
{ // 转换为大写字母,写到发送缓冲区中
g_pucUSBTxBuffer[ulWriteIndex] =
(g_pucUSBRxBuffer[ulReadIndex] - 'a') + 'A';
}
else
{ // 判断是否是大写字母
if((g_pucUSBRxBuffer[ulReadIndex] >= 'A') &&
(g_pucUSBRxBuffer[ulReadIndex] <= 'Z'))
{ // 转换为小写字母,写到发送缓冲区中
g_pucUSBTxBuffer[ulWriteIndex] =
(g_pucUSBRxBuffer[ulReadIndex] - 'Z') + 'z';
}
else
{ // 将接收到的数据拷到发送缓冲区
g_pucUSBTxBuffer[ulWriteIndex] = g_pucUSBRxBuffer[ulReadIndex];
}
}
// 将指针移到下一字节,当移到缓冲区底时,进行对应调整
ulWriteIndex++;
ulWriteIndex = (ulWriteIndex == BULK_BUFFER_SIZE) ? 0 : ulWriteIndex;
ulReadIndex++;
ulReadIndex = (ulReadIndex == BULK_BUFFER_SIZE) ? 0 : ulReadIndex;
ulLoop--;
}
// 完成接收所有字节并进行转换后发送给主机
USBBufferDataWritten(&g_sTxBuffer, ulCount);
DEBUG_PRINT("Wrote %d bytes\n", ulCount);
// 返回处理的字节数
return(ulCount);
}
// 函数void DisplayStatus
// 功能:在屏幕显示当前状态的字符串
// 变量 psContext 指向图形上下文(context)用于OLED屏幕显示
// 变量 pcStatus 指向要显示字符串
Void DisplayStatus(tContext *psContext, char *pcStatus)
{ // 清除原来状态,显示黑色背景
GrContextForegroundSet(&g_sContext, ClrBlack);
GrStringDrawCentered(psContext, " ", -1,
GrContextDpyWidthGet(psContext) / 2, 16, true);
// 显示新状态
DEBUG_PRINT("%s\n", pcStatus);
GrContextForegroundSet(&g_sContext, ClrWhite);
GrStringDrawCentered(psContext, pcStatus, -1,
GrContextDpyWidthGet(psContext) / 2, 16, true);
}
// 函数 ui32 TxHandler
// 功能:处理与发送通道有关的USB设备驱动通知(数据由device向主机传送)
// 变量 pvCBData 是从机提供的返回指针
// 变量 ulEvent 正在被通知的事件
// 变量 ulMsgValue 是事件对应值
// 变量 pvMsgData is 事件指针.
// 此程序被bulk驱动调用以通知USB设备(与发送数据通道有关的)事件
//(IN通道将数据发送给主机)
ui32 TxHandler(void *pvCBData, ui32 ulEvent, ui32 ulMsgValue, void *pvMsgData)
{ // 此程序没有对发送事件作任何响应,只是更新发送字节数
if(ulEvent == USB_EVENT_TX_COMPLETE)
{
g_ulTxCount += ulMsgValue;
}
// 显示Debug信息
DEBUG_PRINT("TX complete %d\n", ulMsgValue);
return(0);
}
//函数ui32 RxHandler
// 功能:处理与发送通道有关的USB设备驱动通知(由主机向device发送)
// 变量 pvCBData 是从机提供的返回指针
// 变量 ulEvent 正在被通知的事件
// 变量 ulMsgValue 是事件对应值
// 变量 pvMsgData is 事件指针.
// 说明:此程序被bulk驱动调用以通知USB设备(与接收数据通道有关的)事件
//(OUT通道将数据由主机发送出去)
ui32 RxHandler(void *pvCBData, ui32 ulEvent,
ui32 ulMsgValue, void *pvMsgData)
{ // 判断是哪一个事件状态
switch(ulEvent)
{ // 正在与主机连接,可以进行通信
case USB_EVENT_CONNECTED:
{
g_bUSBConfigured = true;
g_pcStatus = "Host connected.";
g_ulFlags |= COMMAND_STATUS_UPDATE;
// 刷新缓冲区
USBBufferFlush(&g_sTxBuffer);
USBBufferFlush(&g_sRxBuffer);
break;
}
// 主机断开连接
case USB_EVENT_DISCONNECTED:
{
g_bUSBConfigured = false;
g_pcStatus = "Host disconn.";
g_ulFlags |= COMMAND_STATUS_UPDATE;
break;
}
// 接收到新字节包
case USB_EVENT_RX_AVAILABLE:
{
tUSBDBulkDevice *psDevice;
// 从返回数据变量中回去实例数据的指针
psDevice = (tUSBDBulkDevice *)pvCBData;
// 读取新数据包并且返回发送给主机
return(EchoNewDataToHost(psDevice, pvMsgData, ulMsgValue));
}
// 暂时忽略 挂起(SUSPEND)和重启( RESUME)信号。
case USB_EVENT_SUSPEND:
case USB_EVENT_RESUME:
break;
// 忽略所有信号,程序返回0
default:
break;
}
return(0);
}
// 主程序入口
Int main(void)
{
ui32 ulTxCount;
ui32 ulRxCount;
tRectangle sRect;
char pcBuffer[16];
//使能FPU
ROM_FPULazyStackingEnable();
// 设置系统时钟,50Hz
ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL |
SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
#ifdef DEBUG
// 配置URAT0的相关引脚
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
// 为Debug输出使能UART0.
UARTStdioConfig(0,115200,50000000);
#endif
// USB没有最先被配置
g_bUSBConfigured = false;
// 初始化显示驱动
CFAL96x64x16Init();
// 初始化显示图形环境(context)
GrContextInit(&g_sContext, &g_sCFAL96x64x16);
// OLED屏幕上部为蓝色方块
sRect.i16XMin = 0;
sRect.i16YMin = 0;
sRect.i16XMax = GrContextDpyWidthGet(&g_sContext) - 1;
sRect.i16YMax = 9;
GrContextForegroundSet(&g_sContext, ClrDarkBlue);
GrRectFill(&g_sContext, &sRect);
// OLED屏幕背景为白色文本
GrContextForegroundSet(&g_sContext, ClrWhite);
// 屏幕中间显示应用名称“usb-dev-bulk”
GrContextFontSet(&g_sContext, g_psFontFixed6x8);
GrStringDrawCentered(&g_sContext, "usb-dev-bulk", -1,
GrContextDpyWidthGet(&g_sContext) / 2, 4, 0);
// 显示当前的发送字节数、接收字节数
GrStringDraw(&g_sContext, "Tx bytes:", -1, 0, 32, false);
GrStringDraw(&g_sContext, "Rx bytes:", -1, 0, 42, false);
// 将GPIO外围设备设置为GPIO功能,并且配置USB引脚
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7);
// 使能系统节拍中断(system tick)
ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / SYSTICKS_PER_SECOND);
ROM_SysTickIntEnable();
ROM_SysTickEnable();
// 显示应用程序名称和UART输出
DEBUG_PRINT("\nStellaris USB bulk device example\n");
DEBUG_PRINT("---------------------------------\n\n");
// 显示当前USB设备运行状况
DisplayStatus(&g_sContext, "Configuring USB");
// 初始化USB发送和接收缓冲区
USBBufferInit((tUSBBuffer *)&g_sTxBuffer);
USBBufferInit((tUSBBuffer *)&g_sRxBuffer);
// USB设备初始化
// 将USB设备信息发送给主机USB库并且将设备连接在总线上
USBDBulkInit(0, (tUSBDBulkDevice *)&g_sBulkDevice);
// 等待初始化设置完成
DisplayStatus(&g_sContext, "Waiting for host");
// 清除发送、接收字节数
ulRxCount = 0;
ulTxCount = 0;
// 主程序循环
while(1)
{ // 判断是否请求更新显示状态
if(g_ulFlags & COMMAND_STATUS_UPDATE)
{ // 清除命令标志
g_ulFlags &= ~COMMAND_STATUS_UPDATE;
DisplayStatus(&g_sContext, g_pcStatus);
}
// 判断是否有发送错误
if(ulTxCount != g_ulTxCount)
{ // 更新最后的发送字节数
ulTxCount = g_ulTxCount;
// 通过UART更新显示
usnprintf(pcBuffer, 16, " %d ", ulTxCount);
GrStringDraw(&g_sContext, pcBuffer, -1, 48, 32, true);
}
// 判断是否有接收错误
if(ulRxCount != g_ulRxCount)
{ // 更新最后的接收字节数
ulRxCount = g_ulRxCount;
// 通过UART更新显示
usnprintf(pcBuffer, 16, " %d ", ulRxCount);
GrStringDraw(&g_sContext, pcBuffer, -1, 48, 42, true);
}
}
}