📄 usb.c
字号:
/****************************************************************************
*
* Copyright (c) 2006 by CMX Systems, Inc.
*
* This software is copyrighted by and is the sole property of
* CMX. All rights, title, ownership, or other interests
* in the software remain the property of CMX. This
* software may only be used in accordance with the corresponding
* license agreement. Any unauthorized use, duplication, transmission,
* distribution, or disclosure of this software is expressly forbidden.
*
* This Copyright notice may not be removed or modified without prior
* written consent of CMX.
*
* CMX reserves the right to modify this software without notice.
*
* CMX Systems, Inc.
* 12276 San Jose Blvd. #511
* Jacksonville, FL 32223
* USA
*
* Tel: (904) 880-1840
* Fax: (904) 880-1632
* http: www.cmx.com
* email: cmx@cmx.com
*
***************************************************************************/
#include "hcc_types.h"
#include "mcf5222x_reg.h"
#include "usb.h"
#include "target.h"
#include "uart.h"
/*****************************************************************************
* Local types.
*****************************************************************************/
typedef struct {
volatile hcc_u32 tlength; //需要传输的字节数
volatile hcc_u32 maxlength; //最大传输字节数
void * volatile address; //数据缓冲区地址
volatile usb_callback_t data_func; //
hcc_u16 psize; //端点允许的最大包长度
hcc_u32 data0_tx; //tx触发位
hcc_u32 data0_rx; //rx触发位
volatile hcc_u8 state; //控制端点状态机的状态值
volatile hcc_u8 flags; //端点标识位
//EPFL_ERROR :传输过程中有错误
//EPFL_ZPACKET :最后一个数据包结束后发送0长度的包结束数据阶段
volatile hcc_u8 error; //错误标志
hcc_u8 next_rx; //下一个rx缓冲区
hcc_u8 next_tx; //下一个tx缓冲区
hcc_u8 pad1; //
} ep_info_t;
/*****************************************************************************
* Macro definitions.
*****************************************************************************/
#define DIR_TX BIT7
#define SOF_PID_VALUE 0xd
/* Note: BIT3 shows if the data packet is data0 or data1. */
#define DATA_PID_VALUE 0x3
#define DATA0_PID_VALUE 0x3
#define DATA1_PID_VALUE 0xb
#define MCF_USB_ENDPT(ep) (((hcc_u8*)&MCF_USB_ENDPT0)[ep<<2])
#define MCF_USB_ENDPT_EP_HSHK BIT0
#define MCF_USB_ENDPT_EP_STALL BIT1
#define MCF_USB_ENDPT_EP_TX_EN BIT2
#define MCF_USB_ENDPT_EP_RX_EN BIT3
#define MCF_USB_ENDPT_EP_CTL_DIS BIT4
#define MCF_USB_ENDPT_RETRY_DIS BIT6
#define MCF_USB_ENDPT_HOST_WO_HUB BIT7
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* Control endpoint state machine state values. */
#define EPST_IDLE 0x0
#define EPST_DATA_TX 0x1
#define EPST_DATA_TX_LAST 0x2
#define EPST_DATA_RX 0x3
#define EPST_STATUS_TX 0x4
#define EPST_STATUS_RX 0x5
#define EPST_TX_STOP 0x6
#define EPST_ABORT_TX 0x7
#define EPST_DATA_TX_WAIT_DB 0x8
#define EPST_DATA_TX_EMPTY_DB 0x9
/* Standard USB feature selector values. */
#define FEAT_ENDPOINT_HALT 0u
#define FEAT_DEVICE_REMOTE_WAKEUP 1u
/* Endpoint flag bits. */
#define EPFL_ERROR (hcc_u8)BIT0 /* There was an error during the ongoing
transfer. */
#define EPFL_ZPACKET (hcc_u8)BIT1 /* After the last data packet an additional zero
length packet needs to be transmitted to close
the transfer. */
/* This macro shall evaluate to a uint32 pointer to the start address of the
buffer descriptor table (BDT). The BDT has 32 bytes for each endpoint.
The BDT shall be alignet to 512 byte boundary! */
extern hcc_u32 _BDT_BASE[];
#define BDT_BASE ((hcc_u32*)(_BDT_BASE))
#define BDT_CTL_RX(ep, b) (BDT_BASE[((ep)<<3)+((b)<<1)+0])
#define BDT_ADR_RX(ep, b) (BDT_BASE[((ep)<<3)+((b)<<1)+1])
#define BDT_CTL_TX(ep, b) (BDT_BASE[((ep)<<3)+((b)<<1)+4])
#define BDT_ADR_TX(ep, b) (BDT_BASE[((ep)<<3)+((b)<<1)+5])
#define BDT_CTL_STALL BIT2
#define BDT_CTL_DTS BIT3
#define BDT_CTL_DATA BIT6
#define BDT_CTL_OWN BIT7
/*****************************************************************************
* Global variables.
*****************************************************************************/
/* N/A */
/*****************************************************************************
* Module variables.
*****************************************************************************/
static volatile hcc_u8 usb_current_config;
static volatile hcc_u8 usb_state;
static volatile hcc_u8 new_address;
static ep_info_t ep_info[16];
/*****************************************************************************
* Function predefinitions.
*****************************************************************************/
static void enter_default_state(void);
static void disable_ep_rx(hcc_u8);
static void disable_ep_tx(hcc_u8);
static void ready_ep_rx(hcc_u8, hcc_u8);
static void ready_ep_tx(hcc_u8, hcc_u8);
static hcc_u8 select_tx_buf(hcc_u8 ep);
static void send_zero_packet(hcc_u8 ep);
static void _usb_send(hcc_u8 ep);
static void _usb_receive(hcc_u8 ep);
static callback_state_t cb_set_address(void);
static callback_state_t usb_stm_ctrl0(void);
//usb_get_rx_pptr:取得端点刚接收到数据的缓冲区地址-------------------------*
//功 能: *
// 取得端点刚接收到数据的缓冲区地址,即该端点的BD.ADR_RX * *
//参 数: ep: 端点号 *
//返 回: 端点接收缓冲区的地址 *
//说 明: *
// 由于端点有两个接收缓冲区EVEN和ODD,所以还要根据MCF_USB_STAT.ODD位 *
// 判断上一个接收到的数据是存放在ODD还是EVEN中 *
//-------------------------------------------------------------------------*
hcc_u8* usb_get_rx_pptr(hcc_u8 ep)
{
hcc_u8 cur_buf=(hcc_u8)(MCF_USB_STAT & MCF_USB_STAT_ODD ? 1 : 0);
return((hcc_u8 *)(RD_LE32(&BDT_ADR_RX(ep, cur_buf))));
}
//select_tx_buf:取得端点下一个发送缓冲区的索引-----------------------------*
//功 能: *
// 取得下一个发送缓冲区的索引 * *
//参 数: ep: 端点号 *
//返 回: 下一个tx缓冲区的索引,有效值为0或1 *
//说 明: *
// 由于端点有两个发送缓冲区EVEN和ODD,所以还要判断是选EVEN还是ODD *
//-------------------------------------------------------------------------*
static hcc_u8 select_tx_buf(hcc_u8 ep)
{
/* SHALL ONLY BE CALLED WITH USB INTERRUPTS DISABLED. */
hcc_u8 buf;
hcc_u32 ctl;
//查找BDT中该端点的EVEN或ODD BD是否已经被使用
ctl=(RD_LE32(&BDT_CTL_TX(ep, 0)) & BDT_CTL_OWN)
| (RD_LE32(&BDT_CTL_TX(ep, 1)) & BDT_CTL_OWN) << 1;
switch(ctl)
{
case 0: //两个BD均未被使用
buf=ep_info[ep].next_tx;
ep_info[ep].next_tx ^= 0x1;
break;
case BDT_CTL_OWN: //EVEN BD已经被USB使用
buf=1;
ep_info[ep].next_tx=0;
break;
case BDT_CTL_OWN<<1: //ODD BD已经被USB使用
buf=0;
ep_info[ep].next_tx=1;
break;
default: //两个BD均被使用则报告错误
CMX_ASSERT(0);
}
return(buf);
}
//usb_get_done:返回发送完成的字节数----------------------------------------*
//功 能: *
// 返回发送完成的字节数 * *
//参 数: ep: 端点号 *
//返 回: 发送完成的字节数 *
//-------------------------------------------------------------------------*
hcc_u32 usb_get_done(hcc_u8 ep)
{
return(ep_info[ep].maxlength-ep_info[ep].tlength);
}
//usb_ep_is_busy:判断USB模块是否忙-----------------------------------------*
//功 能: *
// 判断USB模块是否忙 * *
//参 数: ep: 端点号 *
//返 回: 0:空闲,1:忙 *
//-------------------------------------------------------------------------*
hcc_u8 usb_ep_is_busy(hcc_u8 ep)
{
return(ep_info[ep].state != EPST_IDLE ? (hcc_u8)1 : (hcc_u8)0);
}
//usb_get_state:取得USB模块的当前状态--------------------------------------*
//功 能: *
// 取得USB模块的当前状态 * *
//参 数: ep: 端点号 *
//返 回: USB模块的当前状态 *
//-------------------------------------------------------------------------*
hcc_u8 usb_get_state(void)
{
return(usb_state);
}
//usb_ep_error:取得USB模块的错误标识---------------------------------------*
//功 能: *
// 取得USB模块的错误标识 * *
//参 数: ep: 端点号 *
//返 回: 取得USB模块的错误标识 *
//-------------------------------------------------------------------------*
hcc_u8 usb_ep_error(hcc_u8 ep)
{
hcc_u8 tmp=ep_info[ep].error;
ep_info[ep].error=USBEPERR_NONE;
return(tmp);
}
//send_zero_packet:发送0长度的包-------------------------------------------*
//功 能: *
// 控制OUT传输状态阶段的响应 * *
//参 数: ep: 端点号 *
//返 回: 无 *
//说 明: 发送0长度的数据包说明设备的状态是OK的 *
//-------------------------------------------------------------------------*
static void send_zero_packet(hcc_u8 ep)
{
hcc_u8 buf;
buf=select_tx_buf(ep);
WR_LE32(&BDT_CTL_TX(ep, buf), 0);
ready_ep_tx(ep, buf);
}
//USB_Init:USB模块初始化函数-----------------------------------------------*
//功 能: *
// USB模块初始 *
//参 数: ip: 中断优先级 *
// use_alt_clk: 使用的时钟源 *
//返 回:成功返回0,不成功返回非0 *
//说 明: *
// (1)必须在模块初始化完成后才能全能上拉,以供主机识别 *
// (2)调用该函数之前必须确保设置了稳定的USB模块的时钟源 *
//-------------------------------------------------------------------------*
hcc_u8 usb_init(hcc_u8 ip, hcc_u8 use_alt_clk)
{
//退出suspend状态并设定时钟源
MCF_USB_USB_CTRL = (hcc_u8)(use_alt_clk ? 0u : 1u);
//禁止所有USB中断
MCF_USB_INT_ENB = 0x0;
//禁止所有OTG中断
MCF_USB_OTG_INT_EN = 0x0;
//禁止所有USB错误中断
MCF_USB_ERR_ENB = 0x0;
MCF_USB_OTG_CTRL &= ~MCF_USB_OTG_CTRL_DP_HIGH;
//配置USB为默认状态
enter_default_state();
//清除OTG中断标志
MCF_USB_OTG_INT_STAT = 0xff;
//清除USB中断标志
MCF_USB_INT_STAT = 0xff;
//清除USB Error中断标志
MCF_USB_ERR_STAT = 0xff;
//设置中断优先级
MCF_INTC0_ICR53 = ip;
//使能中断控制器中的USB中断
MCF_INTC0_IMRH &= ~MCF_INTC_IMRH_MASK53;
MCF_INTC0_IMRL &= ~ MCF_INTC_IMRL_MASKALL;
//使能USB中断
MCF_USB_INT_ENB = MCF_USB_INT_ENB_SLEEP | MCF_USB_INT_ENB_TOK_DNE
| MCF_USB_INT_ENB_ERROR | MCF_USB_INT_ENB_USB_RST
| MCF_USB_INT_ENB_STALL;
//设定缓冲区描述符表BDT的地址
MCF_USB_BDT_PAGE_01 = (hcc_u8)(((hcc_u32)BDT_BASE) >> 8);
MCF_USB_BDT_PAGE_02 = (hcc_u8)(((hcc_u32)BDT_BASE) >> 16);
MCF_USB_BDT_PAGE_03 = (hcc_u8)(((hcc_u32)BDT_BASE) >> 24);
//使能USB模块
MCF_USB_CTL = MCF_USB_CTL_USB_EN_SOF_EN;
//使能D+和D-上的上拉电阻
//MCF_USB_OTG_CTRL = MCF_USB_OTG_CTRL_DP_HIGH | MCF_USB_OTG_CTRL_DM_HIGH;
MCF_USB_OTG_CTRL = MCF_USB_OTG_CTRL_DP_HIGH|MCF_USB_OTG_CTRL_OTG_EN;
I2CsendByte(0x00, 0x11, 0x58); /*just turn on*/
iic_delay();
I2CsendByte(0x14, 0x10, 0x58); /*FS device */
iic_delay();
return(0);
}
//usb_setup_ep:设置端点函数_-----------------------------------------------*
//功 能: *
// 设置端点 *
//参 数: addr: 端点地址 *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -