This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

清华大学TM4C123微处理器原理与实践例程-USB device实验程序解析



    // 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);
        }
    }
}