//数据采集与记录实验程序解析
//头文件
#include <string.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_sysctl.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/adc.h" //使用ADC驱动函数的头文件
#include "driverlib/hibernate.h" //冬眠模式的API
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/rom.h"
#include "driverlib/pin_map.h"
#include "grlib/grlib.h"
#include "grlib/widget.h"
#include "grlib/canvas.h" //画布控件的原型
#include "usblib/usblib.h" //USB库的主要头文件
#include "drivers/cfal96x64x16.h"
#include "drivers/buttons.h"
#include "utils/ustdlib.h" //简单的标准库函数
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "drivers/slidemenuwidget.h" //滑动窗口菜单的原型
#include "stripchartwidget.h"
#include "clocksetwidget.h"
#include "qs-logger.h"
#include "images.h"
#include "menus.h"
#include "acquire.h"
#include "inc/hw_gpio.h"
#include "inc/hw_hibernate.h" //冬眠模式的定义和宏
#include "usbstick.h"
#include "usbserial.h"
#include "flashstore.h"
#define CLOCK_RATE 100
#define MS_PER_SYSTICK (1000 / CLOCK_RATE)
static volatile unsigned long g_ulTickCount;
//定义计时器中断的时钟速率已经系统时钟节拍的计时器。计时器中断用于
//程序中基本的计时
static unsigned long g_ulKeyFocusWidgetHandle;
//控件的控件处理函数,应该能接受到任何按钮事件
typedef enum
{
STATE_IDLE,
STATE_LOGGING,
STATE_VIEWING,
STATE_SAVING,
STATE_ERASING,
STATE_FREEFLASH,
STATE_CLOCKSET,
STATE_CLOCKEXIT,
} tLoggerState;
static tLoggerState g_sLoggerState = STATE_IDLE;
//记录数据采集器状态
static tConfigState g_sConfigState;
//程序的配置,保存睡眠时需要保存的信息
volatile tUSBMode g_eCurrentUSBMode = USB_MODE_NONE;
//基于探测模式下系统的USB OTG的当前状态
#define HCD_MEMORY_SIZE 128
//主控制器内存字节池的大小
unsigned char g_pHCDPool[HCD_MEMORY_SIZE];
//定义主控制器驱动的内存
static void ClockSetOkCallback(tWidget *pWidget, tBoolean bOk);
//用于时钟设置控件的回调函数的前置声明
//该函数提供一个简单的功能使程序的其它部分可以更新状态显示
void
SetStatusText(const char *pcTitle, const char *pcLine1, const char *pcLine2,
const char *pcLine3)
{
static const char cBlankLine[] = " ";
//以下为检查每个元素是否被传递,如果是,则更新其在状态显示上的文本
pcTitle = pcTitle ? pcTitle : cBlankLine;
MenuUpdateText(TEXT_ITEM_STATUS_TITLE, pcTitle);
pcLine1 = pcLine1 ? pcLine1 : cBlankLine;
MenuUpdateText(TEXT_ITEM_STATUS1, pcLine1);
pcLine2 = pcLine2 ? pcLine2 : cBlankLine;
MenuUpdateText(TEXT_ITEM_STATUS2, pcLine2);
pcLine3 = pcLine3 ? pcLine3 : cBlankLine;
MenuUpdateText(TEXT_ITEM_STATUS3, pcLine3);
//以下为当所有状态文本更新后控件的重新绘制
WidgetPaint(WIDGET_ROOT);
WidgetMessageQueueProcess();
}
//该函数为处理计时器超时中断的函数
void
SysTickIntHandler(void)
{
g_ulTickCount++;
}
//该函数返回从上一次函数调用到现在经历的时钟节拍数
unsigned long
GetTickms(void)
{
unsigned long ulRetVal;
unsigned long ulSaved;
static unsigned long ulLastTick = 0;
ulRetVal = g_ulTickCount;
ulSaved = ulRetVal;
if(ulSaved > ulLastTick)
{
ulRetVal = ulSaved - ulLastTick;
}
else
{
ulRetVal = ulLastTick - ulSaved;
}
ulLastTick = ulSaved;
return(ulRetVal * MS_PER_SYSTICK);
}
static void
ModeCallback(unsigned long ulIndex, tUSBMode eMode)
{
g_eCurrentUSBMode = eMode; //保存新的模式
//USB OTG模式改变的回调函数
switch(eMode)
{
case USB_MODE_HOST:
{
break;
}
case USB_MODE_DEVICE:
{
break;
}
case USB_MODE_NONE:
{
break;
}
default:
{
break;
}
}
}
static int
//该函数作用为从电池供电的内存获取记录器配置。配置从冬眠模块的的
//内存中读取配置,如果有效,函数返回0,如果无效,返回非0 的数。
GetSavedState(tConfigState *pState)
{
unsigned long ulStateLen = sizeof(tConfigState) / 4;
unsigned short usCrc16;
ASSERT(pState);
if(!pState)
{
return(1);
}
//从休眠内存中读取一块到程序状态结构中
HibernateDataGet((unsigned long *)pState, ulStateLen);
if(pState->ulCookie != STATE_COOKIE) //检查缓存状态值是正确的
{
return(1);
}
//找到块的16位循环校验。它存储在最后的位置。
usCrc16 = ROM_Crc16Array(ulStateLen - 1, (const unsigned long *)pState);
//如果循环冗余校验不匹配,则这个块是有错的
if(pState->ulCrc16 != (unsigned long)usCrc16)
{
return(1);
}
return(0);
}
//该函数作用为从电池供电的内存中存储记录器配置。
//配置保存缓存值和一个CRC校验来确定有效性。
static void
SetSavedState(tConfigState *pState)
{
unsigned long ulStateLen = sizeof(tConfigState) / 4;
unsigned short usCrc16;
ASSERT(pState);
if(pState)
{
//将缓存值写入块中
pState->ulCookie = STATE_COOKIE;
usCrc16 = ROM_Crc16Array(ulStateLen - 1,
(const unsigned long *)pState);
//将计算的CRC保存到结构中
pState->ulCrc16 = (unsigned long)usCrc16;
//将整个块写入冬眠存储器中
HibernateDataSet((unsigned long *)pState, ulStateLen);
}
}
static void
//用缺省值填充程序配置
GetDefaultState(tConfigState *pState)
{
ASSERT(pState);
if(pState)
{
MenuGetDefaultState(pState); //从菜单系统中获取缺省值
pState->cFilename[0] = 0; //文件名设置为空
pState->ulFlashStore = 0; //为闪存设置伪地址
pState->ulSleepLogging = 0; //关闭睡眠记录
}
}
//发送按键信息
static void
SendWidgetKeyMessage(unsigned long ulMsg)
{
WidgetMessageQueueAdd(WIDGET_ROOT, ulMsg, g_ulKeyFocusWidgetHandle, 0, 1, 1);
}
//该函数为菜单部件的回调函数。不论何时菜单激活一个与菜单相连的
//子控件,这个函数将被调用。当控件被停用,控制返回到菜单控件是
//也调用这个函数。它可以根据选择的菜单来触发不同的动作,并跟踪应用
//程序和用户界面控制的状态。这个函数在控件树消息处理的内容中被调用,
//故在做任何会影响显示或控件树的动作时要注意。
static void
WidgetActivated(tWidget *pWidget, tSlideMenuItem *pMenuItem,
tBoolean bActivated)
{
char *pcMenuText;
//处理带状图的激活或停用。当用户选择“开始”菜单,
//带状图部件就被激活
if(pWidget == &g_sStripChart.sBase)
{
if(bActivated) //如果带状图被激活,则启动记录器运行
{
//获取当前菜单状态
MenuGetState(&g_sConfigState);
//在电池供电的存储器中保存状态
SetSavedState(&g_sConfigState);
AcquireStart(&g_sConfigState); //启动记录器并更新记录器
g_sLoggerState = STATE_LOGGING;
}
else //如果带状图没有被激活,则停止记录器
{
AcquireStop();
g_sLoggerState = STATE_IDLE;
}
}
//以下为处理用于以数字形式显示获得的数据的容器的激活或者停用,
//这发生在VIEW菜单使用时。
else if((pWidget == &g_sAINContainerCanvas.sBase) ||
(pWidget == &g_sAccelContainerCanvas.sBase) ||
(pWidget == &g_sCurrentContainerCanvas.sBase) ||
(pWidget == &g_sClockContainerCanvas.sBase) ||
(pWidget == &g_sTempContainerCanvas.sBase))
{
if(bActivated) //一个观测计被激活
{
static tConfigState sLocalState;
//获取当前菜单配置状态,将其保存到本地存储中
MenuGetState(&sLocalState);
//以下条语句为修改状态设置适合观测计使用的值,接收速率
//为0.5S。所有的通道被选定。存储介质被设为“viewer”,
//故获得模块将会把采集到的数据希尔合适的观测布。
sLocalState.ucStorage = CONFIG_STORAGE_VIEWER;
sLocalState.ulPeriod = 0x00000040;
sLocalState.usSelectedMask = 0x3ff;
//开启采集模块
AcquireStart(&sLocalState);
g_sLoggerState = STATE_VIEWING;
}
Else //观测器被停用,停止采集模块
{
AcquireStop();
g_sLoggerState = STATE_IDLE;
}
}
//处理状态显示被激活的情况。当任意几个菜单项被选定的时候
//都会发生
else if(pWidget == &g_sStatusContainerCanvas.sBase)
{
//获取当前菜单项的文本指针
if(pMenuItem)
{
pcMenuText = pMenuItem->pcText;
}
else
{
return;
}
//如果激活SAVE菜单,则flash的数据需要被保存到U盘中,
//进入保存状态
if(!strcmp(pcMenuText, "SAVE"))
{
if(bActivated)
{
g_sLoggerState = STATE_SAVING;
}
else
{
g_sLoggerState = STATE_IDLE;
}
}
//如果激活ERASE菜单,则flash数据需要被擦除,进入擦除状态
else if(!strcmp(pcMenuText, "ERASE DATA?"))
{
if(bActivated)
{
g_sLoggerState = STATE_ERASING;
}
else
{
g_sLoggerState = STATE_IDLE;
}
}
//如果激活FLASH SPACE菜单,则用户将会看到flash中剩余空间
//的大小,进入报告状态
else if(!strcmp(pcMenuText, "FLASH SPACE"))
{
if(bActivated)
{
g_sLoggerState = STATE_FREEFLASH;
}
else
{
g_sLoggerState = STATE_IDLE;
}
}
}
//处理时钟设置控件的激活,停用是通过一个分离的回调函数处理的
else if(pWidget == &g_sClockSetter.sBase)
{
unsigned long ulRTC;
if(bActivated) //如果时钟设置被激活,装载时间结构
{
//几秒内从RTC(实时时钟芯片)上获取当前时间
ulRTC = HibernateRTCGet();
//将RTC时间转换成时间结构
ulocaltime(ulRTC, &g_sTimeClock);
//设置时钟设置控件停用时的回调函数。
//由于时钟设置控件需要接管按键事件,当完成的时候需要
//一个单独的回调函数
ClockSetCallbackSet((tClockSetWidget *)pWidget,
ClockSetOkCallback);
//将按键事件交给时钟设置控件
g_ulKeyFocusWidgetHandle = (unsigned long)pWidget;
g_sLoggerState = STATE_CLOCKSET;
}
}
}
//当用户在时钟设置控件中点击OK或CANCEL时调用的函数
static void
ClockSetOkCallback(tWidget *pWidget, tBoolean bOk)
{
unsigned long ulRTC;
if(bOk) //当选择OK按键时,只更新RTC
{
//将时钟设置控件替代的时间格式转换为秒
ulRTC = umktime(&g_sTimeClock);
//如果转换是有效的,则将更新过的时钟写入到冬眠RTC中
if(ulRTC != (unsigned long)(-1))
{
HibernateRTCSet(ulRTC);
}
}
//时钟退出设置状态,主循环可以做一些清理工作
g_sLoggerState = STATE_CLOCKEXIT;
}
//初始化并运行数据记录器
int
main(void)
{
tContext sDisplayContext;
tContext sBufferContext;
unsigned long uX;
unsigned long uY;
unsigned long ulHibIntStatus;
tBoolean bSkipSplash = false;
unsigned long ulSysClock;
FPUEnable();
FPULazyStackingEnable();
ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |SYSCTL_OSC_MAIN);
ulSysClock = ROM_SysCtlClockGet();
//初始化数据采集模块,这初始化ADC硬件。详见acquire.c
AcquireInit();
//使能冬眠外设
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);
//检查冬眠模块是否已经启动,如果启动,就读取保存的配置状态。
//如果都可以了,然后检查我们是否是利用睡眠模式记录数据
if(HibernateIsActive() && !GetSavedState(&g_sConfigState))
{
//读取冬眠模块的状态的中断模式
ulHibIntStatus = HibernateIntStatus(1);
//如果引脚打开,则意味着用户按下选择按键,应该终止睡眠状态的
//记录器。在这种情况下,将跳出这个条件部分,进入下面的正常启
//动,但是需要跳过启动画面,使用户得到立即响应
if(ulHibIntStatus & HIBERNATE_INT_PIN_WAKE)
{
HibernateIntClear(HIBERNATE_INT_PIN_WAKE); //清除中断标志
bSkipSplash = 1;
}
//否则如果从冬眠状态缓解,但不是一个引脚打开,那么它一定是来
//自RTC匹配。检查我们是否是在睡眠状态的记录,如果是这样,进
//行一个简短的启动来采集数据,然后再进入睡眠状态
else if(g_sConfigState.ulSleepLogging &&
(ulHibIntStatus & HIBERNATE_INT_RTC_MATCH_0))
{
//启动记录器,传递配置。记录器应该配置成采集一个样本
AcquireStart(&g_sConfigState);
g_sLoggerState = STATE_LOGGING;
//进入无限循环,运行采集,直到一个新的样本被采集并存储
while(!AcquireRun())
{
}
//数据采集已经结束,可以进入继续睡眠状态。
//保存配置,然后激活冬眠。
SetSavedState(&g_sConfigState);
//设置唤醒引脚或RTC匹配的条件,然后使进程处于冬眠状态
HibernateWakeSet(HIBERNATE_WAKE_PIN | HIBERNATE_WAKE_RTC);
HibernateRequest();
//永远等待直到冬眠激活,处理器能源移除
for(;;)
{
}
}
}
//否则,或者冬眠模块没有激活,或者保存的配置不正确。
//将配置初始化到缺省状态。
else
{
GetDefaultState(&g_sConfigState);
}
HibernateEnableExpClk(SysCtlClockGet()); //启动冬眠模块
HibernateRTCTrimSet(0x7FFF); //设置RTC predivider trim寄存器的值
HibernateRTCEnable(); //启动RTC
g_sConfigState.ulSleepLogging = 0;
SetSavedState(&g_sConfigState);
CFAL96x64x16Init();
ButtonsInit(); //初始化按键驱动
MenuSetState(&g_sConfigState); //将存储的状态传递给菜单系统
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); //使能USB
//为USB运行配置需要的引脚
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
ROM_GPIOPinConfigure(GPIO_PG4_USB0EPEN);
ROM_GPIOPinTypeUSBDigital(GPIO_PORTG_BASE, GPIO_PIN_4);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
if(CLASS_IS_BLIZZARD && REVISION_IS_A1) //Erratum workaround for silicon revision A1. VBUS must have pull-down
{
HWREG(GPIO_PORTB_BASE + GPIO_O_PDR) |= GPIO_PIN_1;
}
//初始化USB栈模式
USBStackModeSet(0, USB_MODE_OTG, ModeCallback);
USBStickInit(); //初始化将要被U盘使用的栈
USBSerialInit(); //初始化将要被串行设备使用的栈
//初始化USB控制器为2ms刷新率的双模式操作
USBOTGModeInit(0, 2000, g_pHCDPool, HCD_MEMORY_SIZE);
//初始化菜单模式。这个模式将控制用户界面的菜单系统
MenuInit(WidgetActivated);
//配置时钟产生周期性中断
g_ulTickCount = 0;
ROM_SysTickPeriodSet(ulSysClock / CLOCK_RATE);
ROM_SysTickIntEnable();
ROM_SysTickEnable();
GrContextInit(&sDisplayContext, &g_sCFAL96x64x16);
GrContextInit(&sBufferContext, &g_sOffscreenDisplayA);
//如果不跳过的话就显示启动界面。跳过的唯一的原因是程序在睡眠
//记录模式,用户只是利用select按键将其唤醒
if(!bSkipSplash)
{
const unsigned char *pucSplashLogo = g_pucImage_TI_Black;
//绘制TI logo。动态滑入屏幕。允许select按键中断动画
for(uX = 0; uX < 96; uX++)
{
if(ButtonsPoll(0, 0) & SELECT_BUTTON)
{
break;
}
GrImageDraw(&sDisplayContext, pucSplashLogo, 95 - uX, 0);
}
//让logo在屏幕上留一小段时间。监视按键这样如果用户选择select
//按键,logo显示终止,应用程序立即开始
while(g_ulTickCount < 400)
{
if(ButtonsPoll(0, 0) & SELECT_BUTTON)
{
break;
}
}
if(ButtonsPoll(0, 0) & UP_BUTTON) //扩展启动队列
{
for(uX = 0; uX < 96; uX += 4)
{
GrImageDraw(&sDisplayContext, g_ppucImage_Splash[(uX / 4) & 3], (long)uX - 96L, 0);
GrImageDraw(&sDisplayContext, pucSplashLogo, uX, 0);
ROM_SysCtlDelay(ulSysClock / 12);
}
ROM_SysCtlDelay(ulSysClock / 3);
pucSplashLogo = g_ppucImage_Splash[4];
GrImageDraw(&sDisplayContext, pucSplashLogo, 0, 0);
ROM_SysCtlDelay(ulSysClock / 12);
}
//将初始菜单绘入屏幕外缓冲中
SlideMenuDraw(&g_sMenuWidget, &sBufferContext, 0);
//绘制TI logo启动屏幕同时初始化屏幕上的菜单,移动坐标是logo
//滑出屏幕,菜单滑入屏幕
for(uY = 0; uY < 64; uY++)
{
GrImageDraw(&sDisplayContext, pucSplashLogo, 0, -uY);
GrImageDraw(&sDisplayContext, g_pucOffscreenBufA, 0, 63 - uY);
}
}
//将菜单控件增加到控件树中,发送初始化绘制的请求
WidgetAdd(WIDGET_ROOT, (tWidget *)&g_sMenuWidget);
WidgetPaint(WIDGET_ROOT);
//设置菜单控件的中心处理。任何按键事件都会被送到这个控件
g_ulKeyFocusWidgetHandle = (unsigned long)&g_sMenuWidget;
//无限循环运行程序
while(1)
{
static unsigned long ulLastTickCount = 0;
//每次时钟节拍一次,处理按键事件
if(g_ulTickCount != ulLastTickCount)
{
unsigned char ucButtonState;
unsigned char ucButtonChanged;
ulLastTickCount = g_ulTickCount; //记录上一次节拍计数
//读去抖动的按钮状态
ucButtonState = ButtonsPoll(&ucButtonChanged, 0);
//将任何按键动作传递给控件消息处理机制。
//有按键事件的控件将会获得这些事件
if(BUTTON_PRESSED(SELECT_BUTTON, ucButtonState, ucButtonChanged))
{
SendWidgetKeyMessage(WIDGET_MSG_KEY_SELECT);
}
if(BUTTON_PRESSED(UP_BUTTON, ucButtonState, ucButtonChanged))
{
SendWidgetKeyMessage(WIDGET_MSG_KEY_UP);
}
if(BUTTON_PRESSED(DOWN_BUTTON, ucButtonState, ucButtonChanged))
{
SendWidgetKeyMessage(WIDGET_MSG_KEY_DOWN);
}
if(BUTTON_PRESSED(LEFT_BUTTON, ucButtonState, ucButtonChanged))
{
SendWidgetKeyMessage(WIDGET_MSG_KEY_LEFT);
}
if(BUTTON_PRESSED(RIGHT_BUTTON, ucButtonState, ucButtonChanged))
{
SendWidgetKeyMessage(WIDGET_MSG_KEY_RIGHT);
}
}
//告诉OTG库距离上次调用已经过去多长时间
USBOTGMain(GetTickms());
//根据需要调用函数来保持主机或设备运行
if(g_eCurrentUSBMode == USB_MODE_DEVICE)
{
USBSerialRun();
}
else if(g_eCurrentUSBMode == USB_MODE_HOST)
{
USBStickRun();
}
//如果是在记录状态,则调用记录器运行功能。这保持数据不断采集
if((g_sLoggerState == STATE_LOGGING) ||
(g_sLoggerState == STATE_VIEWING))
{
if(AcquireRun() && g_sConfigState.ulSleepLogging)
{
//如果SLEEP模式启动,则已经存储了第一项数据,现在
//保存数据开始冬眠。等待断电
SetSavedState(&g_sConfigState);
HibernateWakeSet(HIBERNATE_WAKE_PIN | HIBERNATE_WAKE_RTC);
HibernateRequest();
for(;;)
{
}
}
//如果VIEW取代记录,那么需要重绘来保证视图窗口更新
if(g_sLoggerState == STATE_VIEWING)
{
WidgetPaint(WIDGET_ROOT);
}
}
//如果在保存状态,则从闪存中保存数据到U盘
if(g_sLoggerState == STATE_SAVING)
{
FlashStoreSave(); //从flash保存数据到USB
g_sLoggerState = STATE_IDLE; //回到闲置状态
}
//如果是在ERASE状态,擦除flash中的数据
if(g_sLoggerState == STATE_ERASING)
{
FlashStoreErase(); //擦除
g_sLoggerState = STATE_IDLE; //返回闲置状态
}
//如果在flash report状态,则显示已经使用和剩下的闪存的大小
if(g_sLoggerState == STATE_FREEFLASH)
{
FlashStoreReport(); //报告空闲的flash空间
g_sLoggerState = STATE_IDLE;
}
//如果退出时钟设置控件,则意味着控制需要回到菜单系统
if(g_sLoggerState == STATE_CLOCKEXIT)
{
//按键事件给菜单系统
g_ulKeyFocusWidgetHandle = (unsigned long)&g_sMenuWidget;
//发送按键事件到菜单空间,这意味着左键被按下。这标志着
//菜单空间停止当前自空间。菜单空间关闭时钟设置控件,
//恢复对显示屏控制
SendWidgetKeyMessage(WIDGET_MSG_KEY_LEFT);
g_sLoggerState = STATE_IDLE;
}
//处理在控件队列中的任何消息。这使得用户交互运行
WidgetMessageQueueProcess();
}
}