You are on page 1of 23

/////////////////////////////////////////////////////////////////////////

////
pic18_usb.c
////
////
////
//// Hardware layer for CCS's USB library. This hardware layer
////
//// supports the USB peripheral on the PIC18 family chips. Current ////
//// supported families are:
////
////
PIC18F2455/2550/4455/4550
////
////
PIC18F2450/4450
////
////
PIC18F65J50/66J50/66J55/67J50/85J50/86J50/86J55/87J50
////
////
////
//// This file is part of CCS's PIC USB driver code, which includes: ////
//// usb_desc_*.h - an example set of config and device descriptor ////
//// usb.c - USB token and request handler code
////
//// usb.h - definitions, prototypes and global variables
////
////
////
//// The following examples are provided by CCS:
////
//// ex_usb_mouse.c - A HID Mouse.
////
//// ex_usb_hid.c - A custom application using HID protocol.
////
//// ex_usb_kbmouse.c - A HID Mouse/Keyboard combo using multiple ////
////
interfaces.
////
//// ex_usb_kbmouse2.c - A HID Mouse/Keyboard combo using multiple ////
////
HID Reports.
////
//// ex_usb_scope.c - A digital oscilloscope using a custom
////
////
protocol requiring custom Windows drivers. ////
//// ex_usb_serial.c ////
//// ex_usb_serial2.c - Two examples of using the CDC driver for ////
////
a virtual COM port.
////
////
////
//// *********** NOTE ABOUT 18F2450/4450 LIMITATIONS **********
////
//// Due to the limited USB RAM of this family, a limitation of
////
//// this driver is that there are only 3 endpoints (0, 1 and 2). ////
//// The HW actually supports more endpoints, but to simplify
////
//// driver development this driver will only support the first 3 ////
//// so there is an easier memory block to work with.
////
////
////
//// USB_MAX_EP0_PACKET_LENGTH will also be set to 8 regardless
////
//// of USB speed, to save RAM.
////
////
////
//// ************** NOTE ABOUT HW REQUIREMENTS ****************
////
//// If you are not using internal pullups, you will need to put
////
//// an internal pullup resistor on C4 or C5 depending on if you
////
//// want to use slow speed or full speed. This code configures
////
//// the device to use internal pullups, see usb_init() if you
////
//// want to change that.
////
////
////
//// You need approximately 470nF cap on C3, even if you are using ////
//// the internal 3.3V USB regulator.
////
////
////
//// To run at full speed, you must use the oscillator
////
//// configuration (PLLx) to set the PLL divide to 4MHz. You can ////
//// configure the MCU clock to any speed (up to 48MHz) but the
////
//// PLL must run at 4Mhz to provide the USB peripheral with a
////
//// 96MHz clock. See the datasheet for details.
////
////
////
//// To run at slow speed you must configure your MCU to run at
////
//// 24Mhz. See the datasheet for details.
////
////
////
//// **************** NOTE ABOUT INTERRUPTS ****************** ////
//// This driver uses INT_USB. It requires INT_USB to interrupt the ////
//// PIC when an event has happened on the USB Bus. Therfore
////

//// this code enables interrupts. A user modification can be made ////
//// to poll the USB interrupt flag instead of relying on an
////
//// interrupt.
////
////
////
////
**************** USER FUNCTIONS *********************** ////
////
////
//// usb_init() - Initializes the USB stack, the USB peripheral and ////
////
attaches the unit to the usb bus. Enables
////
////
interrupts.
////
////
////
//// usb_init_cs() - A smaller usb_init(), does not attach unit
////
////
to usb bus or enable interrupts.
////
////
////
//// usb_put_packet() - Sends one packet to the host.
////
////
If you need to send a message that spans
////
////
more than one packet then see usb_puts() in ////
////
usb.c
////
////
////
//// usb_kbhit() - Returns true if OUT endpoint contains data from ////
////
host.
////
////
////
//// usb_rx_packet_size() - Returns the size of packet that was
////
////
received. usb_kbhit() must return TRUE else
////
////
this is not valid. Don't forget in USB there
////
////
are 0 len packets!
////
////
////
//// usb_get_packet() - Gets one packet that from the host.
////
////
usb_kbhit() must return true before you call ////
////
this routine or your data may not be valid. ////
////
Once usb_kbhit() returns true you want to
////
////
call this as soon as possible to get data
////
////
out of the endpoint buffer so the PC can
////
////
start sending more data, if needed.
////
////
This only receives one packet, if you are
////
////
trying to receive a multi-packet message
////
////
see usb_gets() in usb.c.
////
////
////
//// usb_detach() - De-attach USB from the system.
////
////
////
//// usb_attach() - Attach USB to the system.
////
////
////
//// usb_attached() - Returns TRUE if the device is attached to a
////
////
USB cable. A macro that looks at the defined ////
////
connection sense pin.
////
////
////
//// usb_task() - Keeps track of connection sense, calling
////
////
usb_detach() and usb_attach() when needed.
////
////
////
//// For more documentation on these functions read the comments at ////
//// each function.
////
////
////
//// The other functions defined in this file are for use by the
////
//// USB code, and is not meant to be used by the user.
////
////
////
/////////////////////////////////////////////////////////////////////////
////
////
//// Version History:
////
////
////
//// 07-17-07: Added 18F4450,2450 support
////
////
////

//// 07-13-07: Added 87J50 family support


////
////
////
//// 11-01-05: usb_detach(), usb_attach() and usb_init_cs()
////
////
changed for the better.
////
////
////
//// 10-28-05: Added usb_rx_packet_size()
////
////
////
//// 07-13-05: usb_put_packet() changed for 16bit packet sizes
////
////
usb_flush_in() changed for 16bit packet sizes
////
////
usb_get_packet() changed for 16bit packet sizes
////
////
usb_flush_out() changed for 16bit packet sizes
////
////
usb_set_configured() changed for 16bit packet sizes ////
////
////
//// 06-30-05: usb_tbe() added
////
////
The way endpoint 0 DTS is set has been changed.
////
////
////
//// 06-20-05: Initial Release
////
////
////
//// 05-13-05: Beta Release (Full Speed works)
////
////
////
//// 03-21-05: Initial Alpha Release
////
////
////
/////////////////////////////////////////////////////////////////////////
////
(C) Copyright 1996,2005 Custom Computer Services
////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use,
////
//// reproduction or distribution is permitted without written
////
//// permission. Derivative programs created using this software
////
//// in object code form are not restricted in any way.
////
/////////////////////////////////////////////////////////////////////////
#IFNDEF __USB_HARDWARE__
#DEFINE __USB_HARDWARE__
//let the USB Stack know that we are using a PIC with internal USB peripheral
#DEFINE __PIC__ 1
#if ((getenv("DEVICE")=="PIC18F87J50") || (getenv("DEVICE")=="PIC18F86J55") || (
getenv("DEVICE")=="PIC18F86J50") || (getenv("DEVICE")=="PIC18F85J50") || (getenv
("DEVICE")=="PIC18F67J50") || (getenv("DEVICE")=="PIC18F66J55") || (getenv("DEVI
CE")=="PIC18F66J50") || (getenv("DEVICE")=="PIC18F65J50"))
#define __USB_87J50__
#define USB_TOTAL_BUFFER_SPACE ((int16)getenv("RAM")-0x500)
#define USB_MAX_NUM_ENDPOINTS 16
#elif ((getenv("DEVICE")=="PIC18F2450") || (getenv("DEVICE")=="PIC18F4450"))
#define __USB_4450__
#if (USB_EP3_TX_SIZE+USB_EP3_RX_SIZE+USB_EP4_TX_SIZE+USB_EP4_RX_SIZE+USB_EP5_TX
_SIZE+USB_EP5_RX_SIZE+USB_EP6_TX_SIZE+USB_EP6_RX_SIZE+USB_EP7_TX_SIZE+USB_EP7_RX
_SIZE+USB_EP8_TX_SIZE+USB_EP8_RX_SIZE+USB_EP9_TX_SIZE+USB_EP9_RX_SIZE+USB_EP10_T
X_SIZE+USB_EP10_RX_SIZE+USB_EP11_TX_SIZE+USB_EP11_RX_SIZE+USB_EP12_TX_SIZE+USB_E
P12_RX_SIZE+USB_EP13_TX_SIZE+USB_EP13_RX_SIZE+USB_EP14_TX_SIZE+USB_EP14_RX_SIZE+
USB_EP15_TX_SIZE+USB_EP15_RX_SIZE)>0
#error This driver only supports endpoints 0, 1 and 2 for this chip.
#endif
#define USB_MAX_NUM_ENDPOINTS 3
#define USB_TOTAL_BUFFER_SPACE (0x100 - USB_MAX_NUM_ENDPOINTS*8)
#else
#define __USB_4550__
#define USB_TOTAL_BUFFER_SPACE ((int16)0x300)

#define USB_MAX_NUM_ENDPOINTS 16
#endif
#ifndef USB_USE_FULL_SPEED
#define USB_USE_FULL_SPEED
#endif

TRUE

#ifndef USB_CON_SENSE_PIN
#define USB_CON_SENSE_PIN 0
#endif
#if defined(__USB_4450__)
#define USB_MAX_EP0_PACKET_LENGTH 8
#else
#if USB_USE_FULL_SPEED==FALSE
//slow speed requires 8byte max packet size for endpoint 0
#DEFINE USB_MAX_EP0_PACKET_LENGTH 8
#else
//for full speed you can still use 8bytes, but 64 will be faster
#DEFINE USB_MAX_EP0_PACKET_LENGTH 64
#endif
#endif
#INCLUDE <usb.h>
#define USB_BUFFER_NEEDED (USB_EP0_TX_SIZE+USB_EP0_RX_SIZE+USB_EP1_TX_SIZE+USB_E
P1_RX_SIZE+USB_EP2_TX_SIZE+USB_EP2_RX_SIZE+USB_EP3_TX_SIZE+USB_EP3_RX_SIZE+USB_E
P4_TX_SIZE+USB_EP4_RX_SIZE+USB_EP5_TX_SIZE+USB_EP5_RX_SIZE+USB_EP6_TX_SIZE+USB_E
P6_RX_SIZE+USB_EP7_TX_SIZE+USB_EP7_RX_SIZE+USB_EP8_TX_SIZE+USB_EP8_RX_SIZE+USB_E
P9_TX_SIZE+USB_EP9_RX_SIZE+USB_EP10_TX_SIZE+USB_EP10_RX_SIZE+USB_EP11_TX_SIZE+US
B_EP11_RX_SIZE+USB_EP12_TX_SIZE+USB_EP12_RX_SIZE+USB_EP13_TX_SIZE+USB_EP13_RX_SI
ZE+USB_EP14_TX_SIZE+USB_EP14_RX_SIZE+USB_EP15_TX_SIZE+USB_EP15_RX_SIZE)
#if (USB_BUFFER_NEEDED > USB_TOTAL_BUFFER_SPACE)
#error You are trying to allocate more memory for endpoints than the PIC can ha
ndle
#endif
#if defined(__USB_4450__)
#reserve 0x400:0x4FF
#else
#reserve 0x400:0x4FF+USB_BUFFER_NEEDED
#endif
#define debug_usb(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z)
//#define debug_usb printf
//#define debug_putc putc_tbe
#define debug_display_ram(x,y)
/*
void debug_display_ram(int8 len, int8 *ptr) {
int8 max=16;
debug_usb(debug_putc,"%U - ",len);
if (max>len) {max=len;}
while(max--) {
debug_usb(debug_putc,"%X",*ptr);
len--;
ptr++;
}
if (len) {debug_usb(debug_putc,"...");}
}

*/
//if you are worried that the PIC is not receiving packets because a bug in the
//DATA0/DATA1 synch code, you can set this to TRUE to ignore the DTS on
//receiving.
#ifndef USB_IGNORE_RX_DTS
#define USB_IGNORE_RX_DTS FALSE
#endif
#ifndef USB_IGNORE_TX_DTS
#define USB_IGNORE_TX_DTS FALSE
#endif
//if you enable this it will keep a counter of the 6 possible errors the
//pic can detect. disabling this will save you ROM, RAM and execution time.
#ifndef USB_USE_ERROR_COUNTER
#define USB_USE_ERROR_COUNTER FALSE
#endif
#define USB_PING_PONG_MODE_OFF
#define USB_PING_PONG_MODE_E0
#define USB_PING_PONG_MODE_ON

0 //no ping pong


1 //ping pong endpoint 0 only
2 //ping pong all endpoints

//NOTE - PING PONG MODE IS NOT SUPPORTED BY CCS!


#ifndef USB_PING_PONG_MODE
#define USB_PING_PONG_MODE USB_PING_PONG_MODE_OFF
#endif
#if USB_USE_ERROR_COUNTER
int ERROR_COUNTER[6];
#endif
//---pic18fxx5x memory locations
#if defined(__USB_4550__)
#byte UFRML = 0xF66
#byte UFRMH = 0xF67
#byte UIR
= 0xF68
#byte UIE
= 0xF69
#byte UEIR
= 0xF6A
#byte UEIE
= 0xF6B
#byte USTAT = 0xF6C
#byte UCON
= 0xF6D
#byte UADDR = 0xF6E
#byte UCFG
= 0xF6F
#define UEP0_LOC 0xF70
#else
#byte UFRML = 0xF60
#byte UFRMH = 0xF61
#byte UIR
= 0xF62
#byte UIE
= 0xF5C
#byte UEIR
= 0xF63
#byte UEIE
= 0xF5D
#byte USTAT = 0xF64
#byte UCON
= 0xF65
#byte UADDR = 0xF5E
#byte UCFG
= 0xF5F
#define UEP0_LOC 0xF4C
#endif
#byte UEP0

= UEP0_LOC

#if defined(__USB_4450__)
#define USB_BUFFER (0x400 + (USB_MAX_NUM_ENDPOINTS*8))
//#define USB_BUFFER 0x418 //if you have an old compiler you will need to use
this
#else
#define USB_BUFFER 0x500
#endif
#byte
#byte
#byte
#byte

BD0STAT
BD0CNT
BD0ADRL
BD0ADRJ

#define
#define
#define
#define

= 0x400
= 0x401
= 0x402
= 0x403

BD0STAT_LOC
BD0CNT_LOC
BD0ADRL_LOC
BD0ADRH_LOC

0x400
0x401
0x402
0x403

#define UEP(x) *(UEP0_LOC+x)


#BIT
#BIT
#BIT
#BIT
#BIT
#BIT
#BIT

UIR_SOF = UIR.6
UIR_STALL = UIR.5
UIR_IDLE = UIR.4
UIR_TRN = UIR.3
UIR_ACTV = UIR.2
UIR_UERR = UIR.1
UIR_URST = UIR.0

#BIT
#BIT
#BIT
#BIT
#BIT
#BIT
#BIT

UIE_SOF = UIE.6
UIE_STALL = UIE.5
UIE_IDLE = UIE.4
UIE_TRN = UIE.3
UIE_ACTV = UIE.2
UIE_UERR = UIE.1
UIE_URST = UIE.0

#bit
#bit
#bit
#bit
#bit
#bit

UCON_PBRST=UCON.6
UCON_SE0=UCON.5
UCON_PKTDIS=UCON.4
UCON_USBEN=UCON.3
UCON_RESUME=UCON.2
UCON_SUSPND=UCON.1

#if (USB_PING_PONG_MODE==USB_PING_PONG_MODE_OFF)
#define EP_BDxST_O(x)
*(BD0STAT_LOC + x*8)
#define EP_BDxCNT_O(x)
*(BD0CNT_LOC + x*8)
#define EP_BDxADR_O(x) *(int16 *)(BD0ADRL_LOC + x*8)
#define EP_BDxST_I(x)
*(BD0STAT_LOC + 4 + x*8)
#define EP_BDxCNT_I(x)
*(BD0CNT_LOC + 4 + x*8)
#define EP_BDxADR_I(x) *(int16 *)(BD0ADRL_LOC + 4 + x*8)
#else
#error Right now this driver only supports no ping pong
#endif
//See UEPn (0xF70-0xF7F)
#define ENDPT_DISABLED 0x00
#define ENDPT_IN_ONLY 0x02
#define ENDPT_OUT_ONLY 0x04
#define ENDPT_CONTROL 0x06

//endpoint not used


//endpoint supports IN transactions only
//endpoint supports OUT transactions only
//Supports IN, OUT and CONTROL transactions - On

ly use with EP0


#define ENDPT_NON_CONTROL 0x0E //Supports both IN and OUT transactions
//Define the states that the USB interface can be in
enum {USB_STATE_DETACHED=0, USB_STATE_ATTACHED=1, USB_STATE_POWERED=2, USB_STATE
_DEFAULT=3,
USB_STATE_ADDRESS=4, USB_STATE_CONFIGURED=5} usb_state=0;
//--BDendST has their PIDs upshifed 2
#define USB_PIC_PID_IN
0x24 //device to host transactions
#define USB_PIC_PID_OUT
0x04 //host to device transactions
#define USB_PIC_PID_SETUP
0x34 //host to device setup transaction
#define USTAT_IN_E0
4
#define USTAT_OUT_SETUP_E0 0
#define
#define
#define
#define
#define
#define
#define

__USB_UIF_RESET
__USB_UIF_ERROR
__USB_UIF_ACTIVE
__USB_UIF_TOKEN
__USB_UIF_IDLE
__USB_UIF_STALL
__USB_UIF_SOF

0x01
0x02
0x04
0x08
0x10
0x20
0x40

#if USB_USE_ERROR_COUNTER
#define STANDARD_INTS 0x3F
#else
#define STANDARD_INTS 0x3D
#endif
#define __USB_UCFG_UTEYE
#if defined(__USB_4550__)
#define __USB_UCFG_UOEMON
#endif
#define __USB_UCFG_UPUEN
#define __USB_UCFG_UTRDIS
#define __USB_UCFG_FSEN

0x80
0x40
0x10
0x08
0x04

#if USB_USE_FULL_SPEED
#define __UCFG_VAL_ENABLED__ (__USB_UCFG_UPUEN | __USB_UCFG_FSEN | USB_PING_P
ONG_MODE)
#else
#define __UCFG_VAL_ENABLED__ (__USB_UCFG_UPUEN | USB_PING_PONG_MODE);
#endif
#define __UCFG_VAL_DISABLED__ 0x08
char usb_ep0_rx_buffer[USB_MAX_EP0_PACKET_LENGTH];
#locate usb_ep0_rx_buffer=USB_BUFFER
char usb_ep0_tx_buffer[USB_MAX_EP0_PACKET_LENGTH];
#locate usb_ep0_tx_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH
char usb_data_buffer[USB_TOTAL_BUFFER_SPACE-USB_MAX_EP0_PACKET_LENGTH-USB_MAX_EP
0_PACKET_LENGTH];
#locate usb_data_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH+USB_MAX_EP0_PACKET_
LENGTH
int8 __setup_0_tx_size;

//interrupt handler, specific to PIC18Fxx5x peripheral only


void usb_handle_interrupt();
void usb_isr_rst();
void usb_isr_uerr();
void usb_isr_sof(void);
void usb_isr_activity();
void usb_isr_uidle();
void usb_isr_tok_dne();
void usb_isr_stall(void);
void usb_init_ep0_setup(void);
//following functions standard part of CCS PIC USB driver, and used by usb.c
void usb_init();
void usb_detach();
int1 usb_put_packet(int endpoint, int * ptr, int16 len, USB_DTS_BIT tgl);
int1 usb_flush_in(int8 endpoint, int16 len, USB_DTS_BIT tgl); //marks the transm
it buffer as ready for transmission
int16 usb_get_packet(int8 endpoint, int8 * ptr, int16 max);
int16 usb_rx_packet_size(int8 endpoint);
int16 usb_get_packet_buffer(int8 endpoint, int8 *ptr, int16 max);
void usb_flush_out(int8 endpoint, USB_DTS_BIT tgl);
void usb_stall_ep(int8 endpoint);
void usb_unstall_ep(int8 endpoint);
int1 usb_endpoint_stalled(int8 endpoint);
void usb_set_address(int8 address);
void usb_set_configured(int config);
void usb_disable_endpoints(void);
//// BEGIN User Functions:
/******************************************************************************
/* usb_attached()
/*
/* Summary: Returns TRUE if the device is attached to a USB cable
/*
/*****************************************************************************/
#if USB_CON_SENSE_PIN
#define usb_attached() input(USB_CON_SENSE_PIN)
#else
#define usb_attached() TRUE
#endif
/******************************************************************************
/* usb_detach()
/*
/* Summary: Remove the D+/D- lines from the USB bus. Basically, disable USB.
/*
/*****************************************************************************/
void usb_detach(void) { //done
UCON=0; //disable USB hardware
UIE=0; //disable USB interrupts
UCFG = __UCFG_VAL_DISABLED__;
set_tris_c(*0xF94 | 0x30);
usb_state=USB_STATE_DETACHED;
usb_token_reset();
//clear the chapter9 stack
__usb_kbhit_status=0;
}
/******************************************************************************
/* usb_attach()

/*
/* Summary: Attach the D+/D- lines to the USB bus. Enable the USB peripheral.
/*
/* You should wait until UCON_SE0 is clear before enabling reset/idle interrupt
/*
/*****************************************************************************/
void usb_attach(void) {
usb_token_reset();
UCON = 0;
UCFG = __UCFG_VAL_ENABLED__;
UIE = 0;
// Mask all USB interrupts
UCON_USBEN = 1;
// Enable module & attach to bus
usb_state = USB_STATE_ATTACHED;
// Defined in usbmmap.c & .h
}
/*****************************************************************************
/* usb_init_cs()
/*
/* Summary: Resets and initalizes USB peripheral. Does not attach the periphera
l
/*
to the USB bus. See usb_attach() and usb_task() on how to
/*
attach to the USB bus.
/*
/*
You must call this before any other USB code.
/*
/*
NOTE: an alternative function, usb_init(), is provided that
/*
initializes the USB and then connects.
/*
/*****************************************************************************/
#define usb_init_cs usb_detach
/*****************************************************************************
/* usb_task()
/*
/* Summary: Keeps an eye on the connection sense pin to determine if we are
/*
attached to a USB cable or not. If we are attached to a USB cable,
/*
initialize the USB peripheral if needed. If we are disconnected
/*
from the USB cable, disable the USB peripheral.
/*
/*
NOTE: If you are not using a connection sense pin, will automaticall
y
/*
enable the USB peripheral.
/*
/*
NOTE: this enables interrupts once the USB peripheral is ready
/*
/*****************************************************************************/
void usb_task(void) {
if (usb_attached()) {
if (UCON_USBEN==0) {
debug_usb(debug_putc, "\r\n\nUSB TASK: ATTACH");
usb_attach();
}
}
else {
if (UCON_USBEN==1) {
debug_usb(debug_putc, "\r\n\nUSB TASK: DE-ATTACH");
usb_detach();
}
}

if ((usb_state == USB_STATE_ATTACHED)&&(!UCON_SE0)) {
UIR=0;
UIE=0;
enable_interrupts(INT_USB);
enable_interrupts(GLOBAL);
UIE=__USB_UIF_IDLE | __USB_UIF_RESET; //enable IDLE and RESET USB interru
pt
usb_state=USB_STATE_POWERED;
debug_usb(debug_putc, "\r\n\nUSB TASK: POWERED");
}
}
/*****************************************************************************
/* usb_init()
/*
/* Summary: Resets and initalizes USB hardware. You must call this first before
/*
using code. Will attach the USB periperhal to the USB bus.
/*
/*
NOTE: If you are using a connection sense pin, this will wait in
/*
an infinite loop until the device is connected to a USB cable.
/*
/*
NOTE: If you are not using a connection sense pin, this will wait
/*
in an infinte loop until the SE0 condition clears, which usual
ly
/*
doesn't take long
/*
/*
NOTE: this enables interrupts.
/*
/*****************************************************************************/
void usb_init(void) {
usb_init_cs();
do {
usb_task();
} while (usb_state != USB_STATE_POWERED);
}
/**************************************************************
/* usb_flush_in()
/*
/* Input: endpoint - which endpoint to mark for transfer
/*
len - length of data that is being tramsferred
/*
tgl - Data toggle synchronization for this packet
/*
/* Output: TRUE if success, FALSE if error (we don't control the endpoint)
/*
/* Summary: Marks the endpoint ready for transmission. You must
/*
have already loaded the endpoint buffer with data.
/*
(IN is PIC -> PC)
/***************************************************************/
int1 usb_flush_in(int8 endpoint, int16 len, USB_DTS_BIT tgl) {
int8 i;
debug_usb(debug_putc,"\r\nPUT %X %U %LU",endpoint, tgl, len);
i=EP_BDxST_I(endpoint);
if (!bit_test(i,7)) {
EP_BDxCNT_I(endpoint)=len;

debug_display_ram(len, EP_BDxADR_I(endpoint));
#if USB_IGNORE_TX_DTS
i=0x80;
#else
if (tgl == USB_DTS_TOGGLE) {
i=EP_BDxST_I(endpoint);
if (bit_test(i,6))
tgl=USB_DTS_DATA0; //was DATA1, goto DATA0
else
tgl=USB_DTS_DATA1; //was DATA0, goto DATA1
}
else if (tgl == USB_DTS_USERX) {
i=EP_BDxST_O(endpoint);
if (bit_test(i,6))
tgl=USB_DTS_DATA1;
else
tgl=USB_DTS_DATA0;
}
if (tgl == USB_DTS_DATA1) {
i=0xC8; //DATA1, UOWN
}
else if (tgl == USB_DTS_DATA0) {
i=0x88; //DATA0, UOWN
}
#endif
//set BC8 and BC9
if (bit_test(len,8)) {bit_set(i,0);}
if (bit_test(len,9)) {bit_set(i,1);}
debug_usb(debug_putc," %X",i);
EP_BDxST_I(endpoint)=i;//save changes
return(1);
}
else {
debug_usb(debug_putc,"\r\nPUT ERR");
}
return(0);
}
/*******************************************************************************
/* usb_put_packet(endpoint,*ptr,len,toggle)
/*
/* Input: endpoint - endpoint to send packet to
/*
ptr - points to data to send
/*
len - amount of data to send
/*
toggle - whether to send data with a DATA0 pid, a DATA1 pid, or toggle
from the last DATAx pid.
/*
/* Output: TRUE if data was sent correctly, FALSE if it was not. The only reaso
n it will
/*
return FALSE is if because the TX buffer is still full from the last
time you
/*
tried to send a packet.
/*
/* Summary: Sends one packet out the EP to the host. Notice that there is a dif

ference
/*
between a packet and a message. If you wanted to send a 512 byte me
ssage you
/*
would accomplish this by sending 8 64-byte packets, followed by a 0
length packet.
/*
If the last (or only packet) being sent is less than the max packet
size defined
/*
in your descriptor then you do not need to send a 0 length packet to
identify
/*
an end of message.
/*
/*
usb_puts() (provided in usb.c) will send a multi-packet message corr
ectly.
/*
/*******************************************************************************
*/
int1 usb_put_packet(int8 endpoint, int8 * ptr, int16 len, USB_DTS_BIT tgl) { //d
one
int16 j;
int8 i;
int8 * buff_add;
i=EP_BDxST_I(endpoint);
if (!bit_test(i,7)) {
buff_add=EP_BDxADR_I(endpoint);
for (j=0;j<len;j++) {
*buff_add=*ptr;
buff_add++;
ptr++;
}
return(usb_flush_in(endpoint, len, tgl));
}
else {
debug_usb(debug_putc,"\r\nPUT ERR");
}
return(0);
}
/// END User Functions
/// BEGIN Hardware layer functions required by USB.C
/**************************************************************
/* usb_flush_out()
/*
/* Input: endpoint - which endpoint to mark for transfer
/*
tgl - Data toggle synchronization to expect in the next packet
/*
/* Output: NONE
/*
/* Summary: Clears the previously received packet, and then marks this
/*
endpoint's receive buffer as ready for more data.
/*
(OUT is PC -> PIC)
/***************************************************************/
void usb_flush_out(int8 endpoint, USB_DTS_BIT tgl) {
int8 i;

int16 len;
#if USB_IGNORE_RX_DTS
if (tgl == USB_DTS_STALL) {
debug_usb(debug_putc, '*');
i=0x84;
EP_BDxST_I(endpoint)=0x84;
return;
}
else
i=0x80;
#else
i=EP_BDxST_O(endpoint);
if (tgl == USB_DTS_TOGGLE) {
if (bit_test(i,6))
tgl=USB_DTS_DATA0; //was DATA1, goto DATA0
else
tgl=USB_DTS_DATA1; //was DATA0, goto DATA1
}
if (tgl == USB_DTS_STALL) {
i=0x84;
EP_BDxST_I(endpoint)=0x84; //stall both in and out endpoints
}
else if (tgl == USB_DTS_DATA1) {
i=0xC8; //DATA1, UOWN
}
else if (tgl == USB_DTS_DATA0) {
i=0x88; //DATA0, UOWN
}
#endif
bit_clear(__usb_kbhit_status,endpoint);
len=usb_ep_rx_size[endpoint];
EP_BDxCNT_O(endpoint)=len;
if (bit_test(len,8)) {bit_set(i,0);}
if (bit_test(len,9)) {bit_set(i,1);}
EP_BDxST_O(endpoint)=i;
}
int16 usb_rx_packet_size(int8 endpoint) {
return(EP_BDxCNT_O(endpoint));
}
/*******************************************************************************
/* usb_get_packet_buffer(endpoint, *ptr, max)
/*
/* Input: endpoint - endpoint to get data from
/*
ptr - where to save data to local PIC RAM
/*
max - max amount of data to receive from buffer
/*
/* Output: the amount of data taken from the buffer.
/*
/* Summary: Gets a packet of data from the USB buffer and puts into local PIC RA
M.
/*
Does not mark the endpoint as ready for more data. Once you are
/*
done with data, call usb_flush_out() to mark the endpoint ready
/*
to receive more data.

/*
/*******************************************************************************
*/
int16 usb_get_packet_buffer(int8 endpoint, int8 *ptr, int16 max) {
int8 * al;
int8 st;
int16 i;
al=EP_BDxADR_O(endpoint);
i=EP_BDxCNT_O(endpoint);
st=EP_BDxST_O(endpoint);
//read BC8 and BC9
if (bit_test(st,0)) {bit_set(i,8);}
if (bit_test(st,1)) {bit_set(i,9);}
if (i<max) {max=i;}
i=0;
while (i<max) {
*ptr=*al;
ptr++;
al++;
i++;
}
return(max);
}
/*******************************************************************************
/* usb_get_packet(endpoint, *ptr, max)
/*
/* Input: endpoint - endpoint to get data from
/*
ptr - where to save data to local PIC RAM
/*
max - max amount of data to receive from buffer
/*
/* Output: the amount of data taken from the buffer.
/*
/*
NOTE - IF THERE IS NO PACKET TO GET YOU WILL GET INVALID RESULTS!
/*
VERIFY WITH USB_KBHIT() BEFORE YOU CALL USB_GET_PACKET()!
/*
/* Summary: Gets a packet of data from the USB buffer and puts into local PIC RA
M.
/*
Until you call usb_get_packet() the data will sit in the endpoint
/*
buffer and the PC will get NAKs when it tries to write more data
/*
to the endpoint.
/*
/*******************************************************************************
*/
int16 usb_get_packet(int8 endpoint, int8 * ptr, int16 max) {
max=usb_get_packet_buffer(endpoint,ptr,max);
usb_flush_out(endpoint, USB_DTS_TOGGLE);
return(max);
}
/*******************************************************************************
/* usb_tbe(endpoint)

/*
/* Input: endpoint - endpoint to check
/*
ptr - where to save data to local PIC RAM
/*
max - max amount of data to receive from buffer
/*
/* Output: returns TRUE if this endpoint's IN buffer (PIC-PC) is empty and ready
/*
returns FALSE if this endpoint's IN buffer is still processing the la
st
/*
transmit or if this endpoint is invalid.
/*
/*******************************************************************************
*/
int8 usb_tbe(int8 endpoint) {
int8 st;
st=EP_BDxST_I(endpoint);
if (!bit_test(st,7))
return(TRUE);
return(FALSE);
}
/*******************************************************************************
/* usb_stall_ep(endpoint,direction)
/*
/* Input: endpoint - endpoint to stall.
/*
top most bit indicates direction (set is IN, clear is OUT)
/*
/* Summary: Stalls specified endpoint. If endpoint is stalled it will send STAL
L packet
/*
if the host tries to access this endpoint's buffer.
/*
/*
/* NOTE: WE ASSUME ENDPOINT IS VALID. USB.C SHOULD CHECK THIS
/*
/*******************************************************************************
*/
void usb_stall_ep(int8 endpoint) { //done
int1 direction;
direction=bit_test(endpoint,7);
endpoint&=0x7F;
if (direction) {
EP_BDxST_I(endpoint)=0x84;
}
else {
EP_BDxST_O(endpoint)=0x84;
}
}
/*******************************************************************************
/* usb_unstall_ep(endpoint, direction)
/*
/* Input: endpoint - endpoint to un-stall.
/*
top most bit indicates direction (set is IN, clear is OUT)
/*
/* Summary: Un-stalls endpoint.
/*
/* NOTE: WE ASSUME ENDPOINT IS VALID. USB.C SHOULD CHECK THIS
/*******************************************************************************
*/
void usb_unstall_ep(int8 endpoint) { //done
int1 direction;

direction=bit_test(endpoint,7);
endpoint&=0x7F;
if (direction) {
#if USB_IGNORE_RX_DTS
EP_BDxST_I(endpoint)=0x80;
#else
EP_BDxST_I(endpoint)=0x88;
#endif
}
else {
EP_BDxST_O(endpoint)=0x00;
}
}
/*******************************************************************************
/* usb_endpoint_stalled(endpoint)
/*
/* Input: endpoint - endpoint to check
/*
top most bit indicates direction (set is IN, clear is OUT)
/*
/* Output: returns a TRUE if endpoint is stalled, FALSE if it is not.
/*
/* Summary: Looks to see if an endpoint is stalled, or not. Does not look to
/*
see if endpoint has been issued a STALL, just whether or not it is
/*
configured to STALL on the next packet. See Set_Feature and Clear_F
eature
/*
Chapter 9 requests.
/*
/* NOTE: WE ASSUME ENDPOINT IS VALID. USB.C SHOULD CHECK THIS
/*******************************************************************************
*/
int1 usb_endpoint_stalled(int8 endpoint) { //done
int1 direction;
int8 st;
direction=bit_test(endpoint,7);
endpoint&=0x7F;
if (direction) {
st=EP_BDxST_I(endpoint);
}
else {
st=EP_BDxST_O(endpoint);
}
return(bit_test(st,7) && bit_test(st,2));
}
/*******************************************************************************
/* usb_set_address(address)
/*
/* Input: address - address the host specified that we use
/*
/* Summary: Configures the USB Peripheral for the specified device address. The
host
/*
will now talk to use with the following address.
/*
/*******************************************************************************
*/
void usb_set_address(int8 address) { //done
UADDR=address;
if (address) {

usb_state=USB_STATE_ADDRESS;
}
else {
usb_state=USB_STATE_POWERED;
}
}
/*******************************************************************************
/* usb_set_configured(config)
/*
/* Input: config - Configuration to use. 0 to uncofigure device.
/*
/* Summary: Configures or unconfigures device. If configuring device it will
/*
enable all the endpoints the user specified for this configuration.
/*
If un-configuring device it will disable all endpoints.
/*
/*
NOTE: CCS only provides code to handle 1 configuration.
/*
/*******************************************************************************
*/
void usb_set_configured(int config) {
int8 en;
int16 addy;
int8 new_uep;
int16 len;
int8 i;
if (config==0) {
//if config=0 then set addressed state
usb_state=USB_STATE_ADDRESS;
usb_disable_endpoints();
}
else {
usb_state=USB_STATE_CONFIGURED; //else set configed state
addy=(int16)USB_BUFFER+(2*USB_MAX_EP0_PACKET_LENGTH);
for (en=1;en<16;en++) {
new_uep=0;
if (usb_ep_rx_type[en]!=USB_ENABLE_DISABLED) {
new_uep=0x04;
len=usb_ep_rx_size[en];
EP_BDxCNT_O(en)=len;
EP_BDxADR_O(en)=addy;
addy+=usb_ep_rx_size[en];
#if USB_IGNORE_RX_DTS
i=0x80;
#else
i=0x88;
#endif
if (bit_test(len,8)) {bit_set(i,0);}
if (bit_test(len,9)) {bit_set(i,1);}
EP_BDxST_O(en)=i;
}
if (usb_ep_tx_type[en]!=USB_ENABLE_DISABLED) {
new_uep|=0x02;
EP_BDxADR_I(en)=addy;
addy+=usb_ep_tx_size[en];
EP_BDxST_I(en)=0x40;
}
if (new_uep==0x06) {new_uep=0x0E;}
if (usb_ep_tx_type[en]!=USB_ENABLE_ISOCHRONOUS) {

new_uep|=0x10;
}
UEP(en)=new_uep;
}
}
}
/// END Hardware layer functions required by USB.C
/// BEGIN USB Interrupt Service Routine
/*******************************************************************************
/* usb_handle_interrupt()
/*
/* Summary: Checks the interrupt, and acts upon event. Processing finished
/*
tokens is the majority of this code, and is handled by usb.c
/*
/* NOTE: If you wish to change to a polling method (and not an interrupt method)
,
/*
then you must call this function rapidly. If there is more than 10ms
/*
latency the PC may think the USB device is stalled and disable it.
/*
To switch to a polling method, remove the #int_usb line above this fuct
ion.
/*
Also, goto usb_init() and remove the code that enables the USB interrup
t.
/*******************************************************************************
*/
#int_usb
void usb_isr() {
if (usb_state==USB_STATE_DETACHED) return; //should never happen, though
if (UIR) {
debug_usb(debug_putc,"\r\n\n[%X] ",UIR);
if (UIR_ACTV && UIE_ACTV) {usb_isr_activity();} //activity detected. (on
ly enable after sleep)
if (UCON_SUSPND) return;
if (UIR_UERR && UIE_UERR) {usb_isr_uerr();}

//error has been dete

cted
if (UIR_URST && UIE_URST) {usb_isr_rst();}

//usb reset has been det

ected
if (UIR_IDLE && UIE_IDLE) {usb_isr_uidle();}
to sleep
if (UIR_SOF && UIE_SOF) {usb_isr_sof();}
if (UIR_STALL && UIE_STALL) {usb_isr_stall();}
was sent

//idle time, we can go


//a stall handshake

if (UIR_TRN && UIE_TRN) {


usb_isr_tok_dne();
UIR_TRN=0;
// clear the token done interrupt., 0x190.3
}
//a token has been detected (majority of isrs)
}
}
//SOF interrupt not handled. user must add this depending on application
void usb_isr_sof(void) {
debug_usb(debug_putc,"\r\nSOF");

UIR_SOF=0;
}
/*******************************************************************************
/* usb_disable_endpoints()
/*
/* Summary: Disables endpoints 1 thru 15
/*
/*******************************************************************************
*/
void usb_disable_endpoints(void) {
int8 i;
for (i=1;i<16;i++) {
UEP(i)=ENDPT_DISABLED;
}
__usb_kbhit_status=0;
}
/*******************************************************************************
/* usb_isr_rst()
/*
/* Summary: The host (computer) sent us a RESET command. Reset USB device
/*
and token handler code to initial state.
/*
/*******************************************************************************
*/
void usb_isr_rst() {
debug_usb(debug_putc,"R");
UEIR=0;
UIR=0;
UEIE=0x9F;
UIE=STANDARD_INTS & ~__USB_UIF_ACTIVE;
UADDR=0;
usb_token_reset();
usb_disable_endpoints();
UEP(0)=ENDPT_CONTROL | 0x10;
while (UIR_TRN) {
UIR_TRN=0;
//do this to clear out the ustat fifo
}
UCON_PKTDIS=0; //SIE token and packet processing enabled
usb_init_ep0_setup();
usb_state=USB_STATE_DEFAULT; //put usb mcu into default state
}
/*****************************************************************************
/* usb_init_ep0_setup()
/*
/* Summary: Configure EP0 to receive setup packets
/*
/*****************************************************************************/
void usb_init_ep0_setup(void) {

EP_BDxCNT_O(0) = USB_MAX_EP0_PACKET_LENGTH;
EP_BDxADR_O(0) = USB_BUFFER;
#if USB_IGNORE_RX_DTS
EP_BDxST_O(0) = 0x80; //give control to SIE, data toggle synch off
#else
EP_BDxST_O(0) = 0x88; //give control to SIE, DATA0, data toggle synch on
#endif
EP_BDxST_I(0) = 0;
EP_BDxADR_I(0) = USB_BUFFER + (int16)USB_MAX_EP0_PACKET_LENGTH;
}
/*******************************************************************************
/* usb_isr_uerr()
/*
/* Summary: The USB peripheral had an error. If user specified, error counter
/*
will incerement. If having problems check the status of these 8 byt
es.
/*
/* NOTE: This code is not enabled by default.
/*******************************************************************************
*/
void usb_isr_uerr() {
#if USB_USE_ERROR_COUNTER
int ints;
#endif
debug_usb(debug_putc,"E %X ",UEIR);
#if USB_USE_ERROR_COUNTER
ints=UEIR & UEIE; //mask off the flags with the ones that are enabled
if ( bit_test(ints,0) ) { //increment pid_error counter
debug_usb(debug_putc,"PID ");
ERROR_COUNTER[0]++;
}
if ( bit_test(ints,1) ) { //increment crc5 error counter
debug_usbdebug_putc,"CRC5 ");
ERROR_COUNTER[1]++;
}
if ( bit_test(ints,2) ) { //increment crc16 error counter
debug_usb(debug_putc,"CRC16 ");
ERROR_COUNTER[2]++;
}
if ( bit_test(ints,3) ) { //increment dfn8 error counter
debug_usb(debug_putc,"DFN8 ");
ERROR_COUNTER[3]++;
}
if ( bit_test(ints,4) ) { //increment bto error counter
debug_usb(debug_putc,"BTO ");
ERROR_COUNTER[4]++;
}
if ( bit_test(ints,7) ) { //increment bts error counter
debug_usb(debug_putc,"BTS ");

ERROR_COUNTER[5]++;
}
#endif
UEIR=0;
UIR_UERR=0;
}
/*******************************************************************************
/* usb_isr_uidle()
/*
/* Summary: USB peripheral detected IDLE. Put the USB peripheral to sleep.
/*
/*******************************************************************************
*/
void usb_isr_uidle() {
debug_usb(debug_putc,"I");
UIE_ACTV=1; //enable activity interrupt flag. (we are now suspended until w
e get an activity interrupt. nice)
UIR_IDLE=0; //clear idle interrupt flag
UCON_SUSPND=1; //set suspend. we are now suspended
}
/*******************************************************************************
/* usb_isr_activity()
/*
/* Summary: USB peripheral detected activity on the USB device. Wake-up the USB
/*
peripheral.
/*
/*******************************************************************************
*/
void usb_isr_activity() {
debug_usb(debug_putc,"A");
UCON_SUSPND=0; //turn off low power suspending
UIE_ACTV=0; //clear activity interupt enabling
UIR_ACTV=0;
}
/*******************************************************************************
/* usb_isr_stall()
/*
/* Summary: Stall handshake detected.
/*
/*******************************************************************************
*/
void usb_isr_stall(void) {
debug_usb(debug_putc,"S");
if (bit_test(UEP(0),0)) {
usb_init_ep0_setup();
bit_clear(UEP(0),0);
}
UIR_STALL=0;
}
/*******************************************************************************

/* usb_isr_tok_dne()
/*
/* Summary: A Token (IN/OUT/SETUP) has been received by the USB peripheral.
/*
If a setup token on EP0 was received, run the chapter 9 code and
/*
handle the request.
/*
If an IN token on EP0 was received, continue transmitting any
/*
unfinished requests that may take more than one packet to transmit
/*
(if necessary).
/*
If an OUT token on any other EP was received, mark that EP as ready
/*
for a usb_get_packet().
/*
Does not handle any IN or OUT tokens on EP0.
/*
/*******************************************************************************
*/
void usb_isr_tok_dne() {
int8 en;
en=USTAT>>3;
debug_usb(debug_putc,"T ");
debug_usb(debug_putc,"%X ", USTAT);
if (USTAT==USTAT_OUT_SETUP_E0) { //new out or setup token in the buffer
debug_usb(debug_putc,"%X ", EP_BDxST_O(0));
if ((EP_BDxST_O(0) & 0x3C)==USB_PIC_PID_SETUP) {
EP_BDxST_I(0)=0; // return the in buffer to us (dequeue any pendin
g requests)
debug_usb(debug_putc,"(%U) ", EP_BDxCNT_O(0));
debug_display_ram(EP_BDxCNT_O(0), usb_ep0_rx_buffer);
usb_isr_tok_setup_dne();
//if setup_0_tx_size==0xFF - stall ep0 (unhandled request)
//if setup_0_tx_size==0xFE - get EP0OUT ready for a data packet, lea
ve EP0IN alone
//else setup_0_tx_size=size of response, get EP0OUT ready for a setu
p packet, mark EPOIN ready for transmit
if (__setup_0_tx_size==0xFF)
usb_flush_out(0,USB_DTS_STALL);
else {
usb_flush_out(0,USB_DTS_TOGGLE);
if (__setup_0_tx_size!=0xFE) {
usb_flush_in(0,__setup_0_tx_size,USB_DTS_USERX);
}
}
UCON_PKTDIS=0;
// UCON,PKT_DIS ; Assuming there is nothing to
dequeue, clear the packet disable bit
}
else if ((EP_BDxST_O(0) & 0x3C)==USB_PIC_PID_OUT) {
usb_isr_tok_out_dne(0);
usb_flush_out(0,USB_DTS_TOGGLE);
if ((__setup_0_tx_size!=0xFE)&&(__setup_0_tx_size!=0xFF)) {
usb_flush_in(0,__setup_0_tx_size,USB_DTS_DATA1); //send respons
e (usually a 0len)
}
}
}
else if (USTAT==USTAT_IN_E0) {

//pic -> host transfer completed

__setup_0_tx_size=0xFF;
usb_isr_tok_in_dne(0);
if (__setup_0_tx_size!=0xFF)
usb_flush_in(0,__setup_0_tx_size,USB_DTS_TOGGLE);
else
usb_init_ep0_setup();
}
else {
if (!bit_test(USTAT,2)) {
usb_isr_tok_out_dne(en);
}
else {
usb_isr_tok_in_dne(en);
}
}
}
/**************************************************************
/* usb_request_send_response(len)
/* usb_request_get_data()
/* usb_request_stall()
/*
/* Input: len - size of packet to send
/*
/* Summary: After we process a SETUP request, we have 1 of three responses:
/*
1.) send a response IN packet
/*
2.) wait for followup OUT packet(s) with data
/*
3.) stall because we don't support that SETUP request
/*
/*
If we are sending data, the array usb_ep0_tx_buffer[] will hold
/*
the response and the USB Request handler code will call
/*
usb_request_send_response() to let us know how big the packet is.
/*
/*
If we are waiting for more data, usb_request_get_data() will
/*
be called by the USB request handler code to configure the EP0 OUT
/*
endpoint to be ready for more data
/*
/*
If we don't support a request, usb_request_stall() will be called
/*
by the USB request handler code to stall the endpoint 0.
/*
/***************************************************************/
void usb_request_send_response(int len) {
__setup_0_tx_size=len;
}
void usb_request_get_data(void) {
__setup_0_tx_size=0xFE;
}
void usb_request_stall(void) {
__setup_0_tx_size=0xFF;
}
/// END USB Interrupt Service Routine
#ENDIF

You might also like