📄 usbn960x.c
字号:
/////////////////////////////////////////////////////////////////////////
//// usbn960x.c ////
//// ////
//// National USBN960x Hardware layer for CCS's PIC USB driver. ////
//// ////
//// This file is part of CCS's PIC USB driver code, which includes: ////
//// usb_desc.h - an example set of config and device descriptors ////
//// usb.c - USB token and request handler code ////
//// usb.h - definitions, prototypes and global variables ////
//// ////
//// ex_usb_scope.c, an example written specifically for the ////
//// USBN960x, shows how to use the CCS PIC USB driver with the ////
//// USBN960x. ////
//// ////
//// usbn960x.c is the hardware layer level driver for the CCS PIC ////
//// USB driver. You can replace usbn960x.c with another hardware ////
//// layer level and still use the other parts of the PIC USB ////
//// driver. ex_usb_hid.c shows an example of using the Microchip ////
//// PIC16C765 (a PIC with a Slow speed USB Peripheral). ////
//// ////
//// This example USB peripheral layer was written and tested with ////
//// CCS's USB Full Speed demo board. When using your own design, ////
//// either wire your USB960x the same as our demo board or change ////
//// the port and pin definitions below. ////
//// ////
//// ************************* NOTE ************************** ////
//// This driver uses INT_EXT. It requires INT_EXT to interrupt the ////
//// PIC when an event has happened on the USBN960x. Because of ////
//// this code enables interrupts. A user modification can be made ////
//// to poll the USBN960x device instead of relying on an interrupt. ////
//// ////
//// ********************** FUNCTIONS *********************** ////
//// ////
//// usb_init() - Initializes the USB code and USBN960x device. ////
//// NOTE - this enables interrupts. ////
//// ////
//// usb_isr() - Call this if you choose to poll instead of ////
//// interrupt. READ THE COMMENTS AT USB_ISR() FIRST. ////
//// ////
//// 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 ////
//// ////
//// usbn_get_version() - Returns the revision number of the 960x ////
//// ////
//// 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: ////
//// ////
//// October 27th, 2003: RX/TX - IN/OUT Backwards ////
//// in usb_set_configured() ////
//// ////
//// June 18th, 2003: Fixed a problem where PIC would miss interrupt ////
//// ////
//// May 6th, 2003: Fixed a potential read/write to registers ////
//// ////
//// August 2nd, 2002: Initial Public Release ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 1996,2002 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__
#include <usb.h>
#define __USBN__ 1 //let other parts of the code know that we are running a National USB Part
#define USB_MAX_ENDPOINTS 4 //number of bidrectional endpoints. 960x has 1 control port and 6 individual endpoints = 4 bidrectional endpoints
//CCS USB demo board is connected to the USBN960x in this manner:
#ifndef usbn_out
#define usbn_bus_out(x) output_d(x)
#define usbn_bus_in input_d
#define usbn_bus_float() set_tris_d(0xFF)
#define usbn_bus_control() set_tris_d(0)
#endif
//CCS USB demo board is connected to the USBN960x in this manner:
#ifndef USBN_CS
#define USBN_INT PIN_B0 //leave at B0 if you want to use external interrupt
#define USBN_RS PIN_C1 //reset
#define USBN_CS PIN_C2
#define USBN_RD PIN_C3
#define USBN_WR PIN_C4
#define USBN_A0 PIN_C5
#endif
#if defined(__PCM__)
#bit int_ext_flag=0x0B.1
#elif defined(__PCH__)
#bit int_ext_flag=0xFF2.1
#endif
//usbn9603/9604 register set
#define USBN_MCNTRL 0x00
#define USBN_CCONF 0x01
// reserved 0x02
#define USBN_RID 0x03
#define USBN_FAR 0x04
#define USBN_NFSR 0x05
#define USBN_MAEV 0x06
#define USBN_MAMSK 0x07
#define USBN_ALTEV 0x08
#define USBN_ALTMSK 0x09
#define USBN_TXEV 0x0A
#define USBN_TXMSK 0x0B
#define USBN_RXEV 0x0C
#define USBN_RXMSK 0x0D
#define USBN_NAKEV 0x0E
#define USBN_NAKMSK 0x0F
#define USBN_FWEV 0x10
#define USBN_FWMSK 0x11
#define USBN_FNH 0x12
#define USBN_FNL 0x13
#define USBN_DMACNTRL 0x14
#define USBN_DMAEV 0x15
#define USBN_DMAMSK 0x16
#define USBN_MIR 0x17
#define USBN_DMACNT 0x18
#define USBN_DMAERR 0x19
// reserved 0x1A
#define USBN_WKUP 0x1B
// reserved 0x1C
// reserved 0x1D
// reserved 0x1E
// reserved 0x1F
#define USBN_EPC0 0x20
#define USBN_TXD0 0x21
#define USBN_TXS0 0x22
#define USBN_TXC0 0x23
// reserved 0x24
#define USBN_RXD0 0x25
#define USBN_RXS0 0x26
#define USBN_RXC0 0x27
#define USBN_EPC1 0x28
#define USBN_TXD1 0x29
#define USBN_TXS1 0x2A
#define USBN_TXC1 0x2B
#define USBN_EPC2 0x2C
#define USBN_RXD1 0x2D
#define USBN_RXS1 0x2E
#define USBN_RXC1 0x2F
#define USBN_EPC3 0x30
#define USBN_TXD2 0x31
#define USBN_TXS2 0x32
#define USBN_TXC2 0x33
#define USBN_EPC4 0x34
#define USBN_RXD2 0x35
#define USBN_RXS2 0x36
#define USBN_RXC2 0x37
#define USBN_EPC5 0x38
#define USBN_TXD3 0x39
#define USBN_TXS3 0x3A
#define USBN_TXC3 0x3B
#define USBN_EPC6 0x3C
#define USBN_RXD3 0x3D
#define USBN_RXS3 0x3E
#define USBN_RXC3 0x3F
//max packet size of all endpoints, but not EP0
#define USB_MAX_PACKET_LENGTH 64
//CCS turns on the following interrupts:
#ifndef USBN_ALTMSK_ENABLES
#define USBN_ALTMSK_ENABLES 0xC0
#endif
#ifndef USBN_TXMSK_ENABLES
#define USBN_TXMSK_ENABLES 0xFF //interrupt on any change on TX interrupt (IN packet or OVR)
#endif
#ifndef USBN_RXMSK_ENABLES
#define USBN_RXMSK_ENABLES 0xFF //interrupt on any change on RX interrupt (OUT/SETUP packet or OVR)
#endif
#ifndef USBN_NAKMSK_ENABLES
#define USBN_NAKMSK_ENABLES 0x10 //interrupt only on NAK on EP0 OUT/SETUP packets.
#endif
#ifndef USBN_FWMSK_ENABLES
#define USBN_FWMSK_ENABLES 0x00 //dont interrupt at all for frame events
#endif
#ifndef USBN_MAMSK_ENABLES
#define USBN_MAMSK_ENABLES 0xD6 //interrupt on ALT, NAK, TX and RX events
#endif
int8 usbn_current_alt_mask; //we will modify what ALT masks depending if we are idle or not
//NFSR STATES
#define USBN_NFSR_RESET 0
#define USBN_NFSR_RESUME 1
#define USBN_NFSR_OPERATIONAL 2
#define USBN_NFSR_SUSPEND 3
//***************************************************************************************
//* YOU CAN USE THE FOLLOWING 2 CONSTANT ARRAYS TO DEFINE DIFFERENT ENPDOINT MAPPINGS
//*
//* But the way it is setup now is that EPC1 and EPC2 make up USB Endpoint 1,
//* EPC3 and EPC4 make up USB Endpoint 2, and EPC5 and EPC6 make up USB Endpoint 3.
//* EPC0 is always Endpoint 0 (control) and can't be modified.
//* Probably best to leave this way, unless for some reason you need a different
//* endpoint, say you're developing an application that needs to communicate to Endpoint 15.
//****************************************************************************************
//960x has 6 unidrectional endpoint. this maps bidirectional endpoint[1] to 960x EPC1 and EPC2
//when we see IN for EP1 then we know to look at EPC2 and it's respective FIFO buffer
//directions are respective of HOST
const int8 usb_epc_out_address[USB_MAX_ENDPOINTS]={0,1,2,3}; //epc0=0,epc2=1,epc4=2,epc6=3
const int8 usb_epc_in_address[USB_MAX_ENDPOINTS]={0,1,2,3}; //epc0=0,epc1=1,epc3=2,epc5=3
const int8 epc_to_epadd[]={0,1,1,2,2,3,3};
// *** protoypes of user functions
void usb_init(void);
int8 usbn_get_version(void);
// *** prototypes used by usb.c (token handling)
void usb_stall_ep(int8 endpoint, int1 direction);
void usb_unstall_ep(int8 endpoint, int1 direction);
int1 usb_endpoint_stalled(int8 endpoint, int1 direction);
void usb_set_address(int8 address);
void usb_set_configured(int config);
int8 usb_get_packet(int8 endpoint, int8 * ptr, int8 max);
int1 usb_put_packet(int endpoint, int * ptr, int len, PID_TOGGLE tgl);
#inline
void usb_wrongstate();
// *** prototypes (USB ISR)
void usb_isr(void);
void usb_check_warn(void);
void usb_check_alt(void);
void usb_check_alt_reset(void);
void usb_check_txev(void);
void usb_check_frame(void);
void usb_check_nak(void);
void usb_check_uld(void);
void usb_check_rxev(void);
// *** prototype of USBN960x specific functions used only in this file
void usb_reset(void);
void usb_enable_endpoint(int8 endpoint, int1 direction, int1 iso);
void usb_disable_endpoint(int8 endpoint, int1 direction);
void usbn_write(int8 address, int8 data);
int8 usbn_read(int8 address);
int8 usb_find_epc(int8 endpoint, int1 direction);
void usb_clear_ep0_buffer(void);
//DONT CHANGE THE FOLLOWING 3 CONSTANT ARRAYS
const char USBN_EPCx[]={USBN_EPC0,USBN_EPC1,USBN_EPC2,USBN_EPC3,USBN_EPC4,USBN_EPC5,USBN_EPC6}; //epc_address[x] finds the constant USBN_EPCx
const char USBN_TX_FIFOx[]={USBN_TXD0, USBN_TXD1, 0, USBN_TXD2, 0, USBN_TXD3, 0}; //ecp0,ecp1,ecp3,ecp5 have tx_fifos
const char USBN_RX_FIFOx[]={USBN_RXD0, 0, USBN_RXD1, 0, USBN_RXD2, 0, USBN_RXD3}; //ecp0,ecp2,ecp4,ecp6 have rx_fifos
/// BEGIN User Functions
/*******************************************************************************
/* usb_init()
/*
/* Summary: Resets and initalizes USB code and USBN device. You must call this
/* first before using code.
/* If you have debug enabled it will enable TBE interrupts on the PIC.
/*
/* NOTE: this enables interrupts.
/*
/********************************************************************************/
void usb_init(void) {
output_high(USBN_CS);
output_float(USBN_A0);
output_high(USBN_RD);
output_high(USBN_WR);
usbn_bus_float();
output_high(USBN_RS);
delay_ms(100);
output_low(USBN_RS);
delay_ms(10);
output_high(USBN_RS);
delay_ms(10);
usbn_current_alt_mask=USBN_ALTMSK_ENABLES & 0x7F; //make sure resume is off
//turn on masks
usbn_write(USBN_ALTMSK, usbn_current_alt_mask);
usbn_write(USBN_TXMSK, USBN_TXMSK_ENABLES);
usbn_write(USBN_RXMSK, USBN_RXMSK_ENABLES);
usbn_write(USBN_NAKMSK, USBN_NAKMSK_ENABLES);
usbn_write(USBN_FWMSK, USBN_FWMSK_ENABLES);
usbn_write(USBN_MAMSK, USBN_MAMSK_ENABLES);
usbn_write(USBN_EPC0, 0);
usbn_write(USBN_FAR, 0x80);
usbn_write(USBN_WKUP,0x0C);
usbn_write(0x1F,0x40); //3.3V regulator workaround
usb_reset();
delay_ms(10);
ext_int_edge(H_TO_L);
enable_interrupts(GLOBAL);
enable_interrupts(INT_EXT);
#IF USB_DO_DEBUG
enable_interrupts(INT_TBE);
#ENDIF
usbn_write(USBN_MCNTRL, 0x04); //vge
delay_ms(2);
usbn_write(USBN_MCNTRL, 0xCC); //VGE, NAT, active low
}
/*******************************************************************************
/* 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. It will return FALSE if you gave it
/* an invalid endpoint, or because TX buffer is still busy sending last packet. If TX buffer
/* is still busy sending the last packet then you can keep calling usb_put_packet() until it returns
/* TRUE.
/*
/* Summary: Sends a packet out the EP to the host. Notice that there is a difference
/* between a packet and a message. If you wanted to send a 512 byte message 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 correctly.
/*
/********************************************************************************/
int1 usb_put_packet(int endpoint, int * ptr, int len, PID_TOGGLE tgl) {
int8 epc, address, status, tcount;
int8 i;
debug(debug_txb,"\r\nTX %X %X: ",endpoint,len);
if (endpoint < 16) {
epc=usb_find_epc(endpoint,1);
if (epc != 0xFF) {
address=USBN_TX_FIFOx[epc]; //address = USBN_TXDx
address+=2; //TXCx
status=usbn_read(address); //TXCx
if ((!bit_test(status,1))||(!endpoint)) { //make sure last isnt set
status |= 0x08;
usbn_write(address,status); //FLUSH
i=0;
do {
i++;
status=usbn_read(address);
} while ((bit_test(status,3))&&(i!=0)); //wait until flush is clear
address-=2;
if (i==0) {debug_txb('!');}
status=usbn_read(address+1); //TXSx
tcount=status & 0x1F; //find open space on fifo buffer
if (tcount==0) {
debug(debug_txb,"BF ");
return(0);
}
else {
while (len != 0) {
debug(debug_txb,"%X ",*ptr);
usbn_write(address, *ptr);
ptr++;
len--;
}
debug_txb(' ');
if (!endpoint) { //if endpoint 0 we need to disable rx_en on RXC0
status=usbn_read(USBN_RXC0);
status &= 0xFE;
usbn_write(USBN_RXC0, status); //disable RC_EN
}
status=usbn_read(address+2); //TXCx
if (endpoint) {status |= 0x02;} //set LAST bit
if (tgl==1) {status ^= 0x04;} //toggle TOGGLE bit (TOGGLE DATAx)
else if (tgl==0) {status |= 0x04;} //set toggle (DATA1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -