LM3S3748的评估及开发过程之感触
注:本文仅代表本人的评估观点!类似“无责任书评”吧。
最近,有几个设计项目需求都是要求在嵌入式项目中实现USB Device或USB Host功能的。尤其是3G数据交换平台的方案更是要求,用到有USB Host功能的MCU来驱动3G
Modem模块。当然,传统的MCU的方案也不少,S3C2410就是典型的例子。可是S3C2410从系统价格(整体BOM还是偏高一些)到生产(BGA焊接)对于一些产品来说还是有些困难。
凑巧有Luminary的代理商到我这里来推荐新的CortexM3系列MCU。当介绍到LM3S3xxx功能时,它的USB接口功能使我眼前一亮:
50MHz的CortexM3,约等于传统的60MIPS,
USB接口可以配置为Device或Host功能。
演示及开发系统功能完善,有完整的USB 库。
另外,我曾经参与过别的朋友的项目。他们就是用LM3S1xx系列实现的产品,从他们开发的过程看,这系列的MCU对开发系统要求简单。而且,Luminary结构清晰的RDK API分层方式,也是大多数嵌入式开发人员所推崇的。况且他们的那款使用Luminary
MCU的产品已经量产。
嗯,这款MCU值得一弄呀!
通过关系,弄来了原装的LM3S3748
EVB板。精致的塑料盒外包装,打开来一看:有个带LCD的屏的开发板,USB线,一支评估用的U盘,还有评估软件光盘。
安装好光盘的内容,里面的信息还是很全呀,从RDK到AN,Datasheet都有。
先看Datasheet吧,功能都了解一下。这次只要关注接口功能和电源管理两个方面。各方面指标都很满意。尤其是电源管理方面应该是这款MCU的亮点。
重点参考Datasheet的第7章,Hibernation Module部分:
LM3S3748系统电源管理设计简单,MCU只要求IO电源VDD=3.3V,Core电源VDD25=2.5V,模拟ADC部分电源VDDA=3.3V,后备电池电源VBAT=3V。
LM3S3748有Run Mode,Sleep Mode,Deep Sleep Mode,Hibernate Mode四种工作模式。从下表来看,功耗完全符合一般锂电池供电手持式设备的功耗要求。系统进入Hibernate Mode后,HIB输出低,将系统电源的En端拉低,内部只由RTC部分工作。由外部后备电池供电。系统要重新启动,只要将WAKE拉低。HIB就会输出高,系统重新上电,复位。看起来很简单呀!(各位问题就会出在这里,继续关注我的体会吧)。
以下是我几天中学习RDK的一些笔记。当然在开发板上跑例程和USB功能就不用多说了,一切都很顺利。刷LCD也很方便,RDK都整理好了。SSI接口有SPI模式,可以驱动T-Flash卡。而且还在EVB上面跑了uC/OS-II的例程。Luminary的文档功夫确实让咱们佩服。
LM3S3748
封装 :参考Datasheet P674 LQFP-100
GPIO: 参考Datasheet P250 PA0~7,
PB0~7,PC0~7,PD0~7,PE0~7,PF0~7,PG0~7,PH0~3
共40个GPIO。
USB 接口: 参考Datasheet P503
■ 支持USB 2.0 Full-speed Host or Device
■ Three configurable endpoints(1-3) with a
dynamic sizable FIFO support multiple packet queueing.(3 receive and 3 transmit
configurable endpoints)
■ 1 dedicated bi-directional control endpoint
■ 4KB dedicated endpoint memory
■ DMA access to the FIFO allows minimal
interference from system software.
学习步骤:
1.
研究LM3S3748 Driver Library包,
ROM_xxx() 表示利用芯片内部ROM Table中包含的函数,这样可以减少对Flash空间的占用。
也可以使用MAP_xxx()对代码进行Complie-time自动配置,如果芯片内部有ROM 则编译时将MAP_xxx()函数自动编译为ROM_xxx()函数;如果芯片内部无ROM则将MAP_xxx()函数编译为Flash空间中的xxx()函数。
参考
PDL_LM3S-UG-3047.pdf P330 “Mapped
ROM Calls”
2.
EK-LM3S3748_EvalBoard
User’s Guide
EVB上LM3S3748的程序下载是通过FIDI的USB接口,通过FIDI的UART与LM3S3748的Uart0连接。实现从Uart0下载的。???
参考 EK-LM3S3748_EvalBoard.pdf P26 的VCP_RX,VCP_TX信号。
可以使用工具:LM Flash Programmer
从原理图上看是通过FIDI的输出的JTAG,再经过CPLD,将JTAG转给LM3S3748的。
通过Keil
C选择
EVB上的USB接口上有个模拟开关。开关的切换用PH2控制(不是PB0)。
HOSTEn Low= USB Host selected
HOSTEn High= USB
Device selected
3.
USB部分
USB Library 参考 PDL-USBL-UG-3047.pdf
包括四类函数集:
■
Device
和Host应用都会用到的通用目的函数。这些函数包括解析USB Descriptors 和设置应用下的操作模式的函数。
■
Device设备会用到的不依赖设备类特性的函数。例如,Host信号连接及标准Descriptor相应。
■
Host
设备会用到的不依赖设备类特性的函数。例如,Device设备检测及媒举,端点管理。
■
帮助特定USB类设备开发的特定类函数(Class specific functions)和数据类型(data
types)。
PDL_LM3S-UG-3047.pdf
是LM3S DriverLibrary 应用指南。
LM3S的USB controller对应的角色:Device(USBDev),Host(USBHost),OTG(USBOTG),USBEndpoint,USBFIFO。
首先要熟悉BootCode,参考PDL-LM3S-UG-3047.pdf P17 “Boot
Code”
在Keil开发环境下,BootCode在Startup.S文件中。在这个文件中,我们必须分配中断句柄 Interrupt handler 给相关的中断函数。
参考下面的代码:
;******************************************************************************
;
; External
declarations for the interrupt handlers used by the application.
;
;******************************************************************************
EXTERN
IntGPIOa ;要在其它.c 文件中实现IntGPIOa
中断函数
EXTERN
IntGPIOb ;要在其它.c 文件中实现IntGPIOb
中断函数
EXTERN
IntGPIOc ;要在其它.c 文件中实现IntGPIOc
中断函数
Using USB with
the uDMA Controller
uDMA 控制器不能访问控制端点0,其它端点都可以访问。
注意:
USB host IN and
USB device OUT 端点都使用uDMA 接收(receive)
Channel;
USB host OUT and UsB device IN 端点都使用uDMA
发送(transmit) Channel.
在配置端点的同时也需要配置DMA。
USB Controller应用: P285
在DriverLib\usblib 下是USB部分的驱动源码。
Luminary 的USB相关的驱动API函数分为不同级别的4层:
■
Device
Class APIs
■
Device
Class Driver APIs
■
The
USB Device API
■
The
USB DriverLib API
USB DriverLib
API
USB DriverLib是USB设备栈的最底层,全部的源码都在usb.c和usb.h中。由于DriverLib API是USB
Controller硬件寄存器上包的最薄的一层API,因此没有提供任何高层的USB传输支持(例如,端点0传输处理,标准描述符及请求处理,等等)。这层API不适合作应用程序接口。但是很适合用这层API作适合的接口,例如,第三方的USB协议栈。
USB Device API
USB Device API提供了允许开发全特性USB Device应用的一组函数。这些函数多是设备类不相关的。USB Device API支持通过从主机发出的标准请求实现设备媒举。
USB Device API可以用来开发USB设备类驱动。当开发USB Device API时,有时也必须调用USB DriverLib API。
USB Device API的头文件在device/usbdevice.h
Qs-scope 例程就是应用USB
Device API与Windows PC上的应用通讯。
USB Device Class
Driver APIs
Device Class Driver提供了一种高层的USB函数。这些函数在不用考虑USB传输控制的情况下提供一定的USB特性。这些驱动为常用的USB 设备类提供了具有以下特性的高层API函数:
■
容易使用。设备的建立紧调用一系列静态的数据结构而且仅需要调用一个初始API
■
可轻松客户化定制VID/PID,电源参数和字符串表,不用修改任何的库代码。
■
一致的接口。所用的设备类驱动都使用同样的API。这样在各设备类间切换就非常方便了。
■
最少的应用扩展。???
■
可以使用可选的USB Buffer对象来更简化数据传输和接收。使用USB Buffers与Device Class Driver交互可以使其变得像读写API一样简单。而不用考虑用状态机来确认数据是被传输了还是正在发送。
■
Device
Class Driver API完全覆盖了下层的USB Device和USB Driver API,因此在应用开发时只需要一个简单的API接口。
了解了以上的信息,应用开发者在使用Device Classs Driver API时更要注意下面的限制:
■
在应用中仅允许出现一个Device Class Driver
■
现有的class driver 不支持复合设备。如果应用希望支持多于一个USB Class就必须直接调用USB Device API。或者是参考现有的Class Drivers再开发一个复合需要的、Class Driers
■
目前提供的device class drivers不支持转化配置信息。
目前提供的Device Class Driver支持创建通用的Bulk设备,Communication
Device Class(virtual serial port)device和
Human Interface Device Class device(mouse,
keyboard,joystick,etc)
参考PDL-USBL-UG-3047.pdf P134 Using the USB Device API
来写一个USB Mass storge Device设备
如果现有的USB
Device Class Driver不能适合你的应用,就需要用更底层的USB Device API。USB Device API会更灵活的解决更多的问题。使用USB Device API创建一个设备应用会经历下面几步:
■
创建 Device,configuration,interface和endpoint
描述符结构来描述你的设备
■
为你的设备从USB library 接收的每个关心的USB events写事件句柄(Handler)函数
■
调用USB Device API 将设备连接到总线并且管理标准的Host交互。
USB Device API开发相关的几个资源的应用要弄清楚:
1. 描述符如何创建,描述符如何被USB Library调用?
2. USB Buffer 如何分配?
3. USB Event如何理解?各类事件的句柄是如何关联的?
4. uDMA是如何初始化及操作的?
第一步,创建描述符(Building Descriptors)
tDeviceInfo定义:
{
tCustomHandlers sCallbacks;
const unsigned char * pDeviceDescriptor;
const tConfigHeader
*const *ppConfigDescriptors;
const unsigned char *const *ppStringDescriptors;
unsigned long ulNumStringDescriptors;
const tFIFOConfig *psFIFOConfig;
}tDeviceInfo
USB Device API管理所有标准USB描述符。这些描述符通过tDeviceInfo机构中的四个数据域提供给Library。USBDCDInit()会调用tDeviceInfo。相关的数据域是:
■
pDeviceDescriptor
■
ppConfigDescriptors
■
ppStringDescriptors
■
ulNumStringDescriptos
所有的描述符都以指针的形式指向unsigned Character类型的数组。对于特定的描述符的例程可以参考每个USB device class driver主程序源文件(例如,对于generic bulk device class driver 可以参考device/usbdbulk.c)或者在qs-scope
应用例程中的usbdescriptors.c。
tDeviceInfo.pDeviceDescriptor
这个数组保存了USB
Device API返回给Host的GET_DESCRIPTOR(DEVICE)请求的device descriptor。下面是USB
Masstorage device的device descriptor例程:
const unsigned char g_pDeviceDescriptor[]=
{
18, // Size of this
structure.
USB_DTYPE_DEVICE, // Type of
this structure.
USBShort(0x200), // USB
version 2.0
0, // USB Device Class
0, // USB Device
Sub-class
0, // USB Device
protocol
64, // Maximum packet size
for default pipe.
USBShort(0x7104), //
Vendor ID (VID).
USBShort(0xf0ff), // Product ID (PID).
USBShort(0x100), //
Device Version BCD.
1, // Manufacturer
string identifier.
0, // Product string
identifier.
2, // Product serial
number.
1 // Number of
configurations
}
描述符结构中用到的宏定义和标号包含在usblib.h头文件或特定的device
class 头文件,例如:usbhid.h和device/usbhid.h 头文件包含HID
类中的特定值。
tDeviceInfo.ppConfigDescriptors
相对于一个设备描述符而言,多个配置描述符需要通过ppConfigDescriptors指向一个指向tConfigHeader结构的指针数组。pDeviceDescriptor 域中的最后一项定义了配置的数量。
为了更灵活的定义复合设备,不同的配置描述符也定义在一个结构数组中(tConfigHeader)。tConfigHeader结构由数量和一个指向tConfigSection结构数组组成。
typedef struct
{
unsigned
char ucNumSections;
const tConfigSection *const *psSections;
}tConfigHeader
typedef struct
{
unsigned
char ucSize;
const
unsigned char *pucData; //A pointer to a block of data containing an intergral
number of USB descriptors which form part of a larger configuration descriptor;
}tConfigSection
tConfigSection 真正包含了要向Host反馈的配置描述符信息。
下面的例程提供了一个USB Mass Storage Device 的配置描述符结构。这个例程提供了一个包含一个接口和使用两个BULK端点(一个IN,一个OUT)的单一配置。
在本例程中,我们说明了如何使用多个Section去构建一个配置描述符。
注意,描述符域中的wTotalLength 中的值无需准确,因为在最终Build 描述符时USB library会根据Section中的内容自动计算这个值。
const unsigned char g_pMscConfigDescriptor[] =
{
//
//
Configuration descriptor header.
//
9, // Size of the
configuration descriptor.
USB_DTYPE_CONFIGURATION, //
Type of this descriptor.
USBShort(32), // The total size of this full
structure.
1, // The number of
interfaces in this
//
configuration.
1, // The unique value
for this configuration.
0, // The string
identifier that describes this
//
configuration.
0x80, // Bus Powered, Self
Powered, remote wakeup.
0xC8, // The maximum power in 2mA
increments.
//
//
Vendor-specific Interface Descriptor.
//
9, // Size of the
interface descriptor.
USB_DTYPE_INTERFACE, //
Type of this descriptor.
0, // The index for this
interface.
0, // The alternate
setting for this interface.
2, // The number of
endpoints used by this
// interface.
0x08, // The interface class USB Mass Storage
0x06, // The interface sub-class. 使用的子类,为简化块命令
0x50, // The
interface protocol for the sub-class使用的协议,这里使用单批量传输协议
// specified
above.
0, // The string index
for this interface.
//
// Endpoint
Descriptor
//
7, // The size of
the endpoint descriptor.
USB_DTYPE_ENDPOINT,
// Descriptor type is an endpoint.
USB_EP_DESC_IN | USB_EP_TO_INDEX(DATA_IN_ENDPOINT),
USB_EP_ATTR_BULK,
// Endpoint is a bulk endpoint.
USBShort(DATA_IN_EP_MAX_SIZE),
// The maximum packet size. 64bytes
0, // The polling
interval for this endpoint.
//
// Endpoint
Descriptor
//
7, // The size of
the endpoint descriptor.
USB_DTYPE_ENDPOINT,
// Descriptor type is an endpoint.
USB_EP_DESC_OUT | USB_EP_TO_INDEX(DATA_OUT_ENDPOINT),
USB_EP_ATTR_BULK,
// Endpoint is a bulk endpoint.
USBShort(DATA_OUT_EP_MAX_SIZE),
// The maximum packet size. 64bytes
0, // The polling
interval for this endpoint.
};
关于USB Device的中断处理:参考 PDL-USBL-UG-3047.pdf
P139 “USB Event Handlers”
P144 “Interrupt Vector Selection”
typedef struct
{
tCustomHandlers sCallbacks;
const unsigned char * pDeviceDescriptor;
const tConfigHeader
*const *ppConfigDescriptors;
const unsigned char *const *ppStringDescriptors;
unsigned long ulNumStringDescriptors;
const tFIFOConfig *psFIFOConfig;
}tDeviceInfo
USB device 应用主要实现一些响应USB事件的callback函数。这些Callback函数通过USBDCDInit()函数调用的tDeviceInfo.sCallbacks传递给USB Device API。
sCallbacks 是tCustomHandlers类型的数据结构。每个tCustomHandlers都包含一组USB event的函数指针。应用必须为每个关心的Event提供可用函数指针的表。对于不关心的event将对应的函数指针设置为NULL。
typedef struct
{
void (*pfnGetDescriptor)
(unsigned long ulIndex,
tUSBRequest
*pUSBRequest);
tStdRequest pfnRequestHandler;
tInterfaceCallback pfnInterfaceChange;
tInfoCallback pfnConfigChange;
tInfoCallback pfnDataReceived;
tInfoCallback pfnDataSent;
tUSBIntHandler pfnResetHandler;
tUSBIntHandler pfnSuspendHandler;
tUSBIntHandler pfnResumeHandler;
tUSBIntHandler pfnDisconnectHandler;
tUSBEPIntHandler pfnEndpointHandler;
}tCustomHandlers
USB FIFO的配置 参考 PDL-USBL-UG-3047.pdf
P143 “USB
FIFO Configuration”
USB 控制器FIFO 必须被合理的分配给各个端点。尽管实际的配置是通过USB Library完成的,应用程序也必须传递一个结构给USB Library以表示FIFO的配置情况。tFIFOConfig 结构包含两个数组,第一个数组表示3个IN 端点的FIFO配置参数,第二个数组表示3个OUT端点的FIFO配置参数。EP0使用固定的FIFO配置,不需要考虑。
typedef struct
{
tFIFOEntry sIn[NUM_USB_EP-1];
tFIFOEntry sOut[NUM_USB_EP-1];
}tFIFOConfig
一个tFIFOConfig的指针通过tDeviceInfo结构中的psFIFOConfig传递给USBCDCInit()函数。
如果应用不希望使用DMA或双Buffer等特殊的Features,可以使用g_sUSBDefaultFIFOConfig来初始化FIFO。
如果缺省的FIFO配置不能满足,就需要声明一个新的tFIFOConfig结构并用psFIFOConfig指向这个结构。
所有USB Device API的应用程序都必须将中断向量表中的硬件USB控制器的中断函数设置为USB0DeviceIntHandler
经过以上的一番折腾,我觉得自己对LM3S3748有了一定的了解。最近有个手持机的设计需求到了我手上,打开看看。
部分设计需求跟大家分享一下:
系统接口:
1.
LCD
I/F:可以驱动TFT LCD, OLED 128*64, 1.8,2.8, 3.2
2.
USB:
Host x1 接3G Modem
3.
I2S
x1 用来接外部Audio CODEC. 用以支持录音和播放
4.
Audio:
Mic x1, Lineout x1, Speaker out x1
5.
SD
x1 : MicroSD
6.
SPI
x2 : 一路接WiFi,一路接其他外设
7.
马达驱动:水平马达,垂直马达
8.
键盘:5*5键盘(一个独立的开机按键)
9.
RTC
x1
10.I2C
x1:
11.Charger x1
12.Uart x2
电源管理:
1.600mAh锂电池:3.8V电压。支持300mA充电(设计2小时充满),
2.
RTC后备电池:3V,系统支持休眠唤醒模式,支持充电模式。(要注意休眠唤醒切换过程中软件的稳定性)。支持定时开机唤醒功能。
3.
工作续航:6小时。待机续航1周。
再考虑整个系统的BOM成本和量产,开发工具的特点,我觉得比较有底了。决定用LM3S3748来做主控MCU。然后,就是常规的开发步骤:首先,从原理图开始。原理图进行到一半。我忽然觉得应该去Luminary的网站下载最新的资料看看,也许Luminary被TI收购后会有新的变化。下载新的EVB光盘后,仔细看过新Datasheet后,发现errata文件。
相关Errata文档下载
看完之后马上汗下,这款MCU还真是有不少Bug。其中有三条基本是做手持设备不能容忍的:
1.
Hibernation Module管理有问题:VBAT比VDD先上电,系统会有1.6mA的漏电。
2.
休眠唤醒不能Debug!这让人怎么调试这些状态呢。干跑程序?
3.
一些PIN的ESD连HM 2KV都不能达到。
4.
USB接口部分有些很奇怪的要求。懒得细琢磨他们的USB PHY了。
看完这个errata文档,我那个痛苦呀!辛苦半天评估的过程,基本泡汤了。我可不会冒险再继续调试它的电源管理部分了。
手持机项目还要继续…我会选择另一款CortexM3的MCU进行开发,请大家继续关注。
frm说:虽然,LM3S3748从目前的版本看,不太适合手持设备。但是,如果是对电源管理要求不高的系统到是可以尝试。它的特点就是USB应用比较容易上手。
另,我们今后一定要关注各类MCU的最新动态,errata是必看的。这都是原厂的开发人员辛苦调试的结果。也能使我们的开发少走弯路!
相关文档下载:
我未完成的原理图下载,仅供大家参考!
用户243688 2009-11-10 17:35
用户243688 2009-11-10 17:28
用户243688 2009-11-10 17:25