📄 combi.c
字号:
{
if (EP_A0_MODE & SETUP_RECEIVED_MASK) //if a setup was received,
{
DeviceStatus.bAddress &= ~0x80; //clear the address flag
HandleSetup(); //and handle it
}
else if (EP_A0_MODE & IN_RECEIVED_MASK) //if an in was received,
{
HandleIn(); //handle it
if (DeviceStatus.bAddress & 0x80) //if the address flag was set during the setup
//phase preceding this IN,
USB_DEVICE_A = DeviceStatus.bAddress; //enable the new address
DeviceStatus.bAddress &= ~0x80; //and clear the new address flag
}
}
POPX();
POPA();
return;
}
/*
**
** FUNCTION: USB_A_EP1_ISR
**
** PURPOSE: Endpoint 1 ISR.
**
** PARAMETERS: none
**
** DESCRIPTION:
** this routine is entered upon receiving an endpoint 1 interrupt.
** If the ACK bit is sent, indicating that a valid mouse packet was just
** transmitted to the host, the SIE is set to NAK ins, and the
** datatoggle bit is flipped for the next transaction.
*/
void USB_A_EP1_ISR(void)
{
PUSHA();
if (EP_A1_MODE & ( 1 << ACKNOWLEDGE))
{
EP_A1_MODE = USB_MODE_NAK_IN;
EP_A1_COUNTER ^= DATATOGGLE;
}
POPA();
return;
}
/*
**
** FUNCTION: TuneWakeup
**
** PURPOSE: to tune the wakeup ISR
**
** PARAMETERS: none
**
** DESCRIPTION: The wakeup interrupt period is variable by a factor of 5 due to operating and process
** conditions. The period has a prescaler which adjusts the period by a factor of two.
** This routine is called at regular intervals to tune the wakeup ISR to occur at the
** rate at which this routine is called, to within a factor of 2. In other words, if
** this routine is called every 256 msec, it will tune the wakeup ISR to occur at a rate
** between 128-256 msec.
**
**
*/
void TuneWakeup(void)
{
char temp;
temp = CLOCK_CONFIGURATION & TWAKEUP_MASK; //get the current wakeup prescale value
if ((bWakeupCount > 2) && (temp != TWAKEUP_MAX)) //if more than two wakeup ISRs occurred since
//the last time this routine was called, and we
//are not at the maximum prescale value already,
//increment the prescaler to slow down the ISR interval
temp += TWAKEUP_2;
if ((!bWakeupCount) && (temp)) //if no wakeup ISRs occurred, and we are not
//at the minimum prescale value,
//decrement the prescaler to speed up the ISR interval
temp -= TWAKEUP_2;
CLOCK_CONFIGURATION = PRECISION_USB_CLOCKING | temp;
bWakeupCount = 0;
}
/*
**
** FUNCTION: usbmain
**
** PURPOSE: USB main processing loop
**
** PARAMETERS: none
**
** DESCRIPTION:
** main spins in an infinite loop waiting for an event that needs servicing.
** Main is entered from either the power-on reset or the usb bus reset. Both
** of these reset routines insures that all USB variables have been initialized
** prior to calling usbmain.
*/
void usbmain(void)
{
EI();
while (1)
{
//clear watchdog timer
RESET_COP();
ProcessOptics(); //empty the optics queue
if (MsecStatus.b1msFlags & ONE_MSEC_FLAG) //if 1 msec has elapsed
{
if (!MsecStatus.b1msCounter) //every 256 msec tune the wakeup ISR
TuneWakeup();
if (DeviceStatus.bConfiguration &&
(!(MsecStatus.b1msCounter & 3))) //if we're configured, and if 4 msec has elapsed....
MouseTask(); //go handle mouse stuff
if (BusInactive()) //if the bus has gone inactive
Suspend(); //suspend us
MsecStatus.b1msFlags &= ~ONE_MSEC_FLAG;
}
}
}
/*
**
** FUNCTION: Reinitialize
**
** PURPOSE: Reinitializes system RAM and USB engine
**
** PARAMETERS: none
**
** DESCRIPTION:
** This routine initializes all USB variables to their
** default states, and resets USB engine controls to their initial values. RAM
** has been cleared prior to entry.
*/
void UsbReInitialize(void)
{
DI(); //disable ints
RESET_COP(); //reset watchdog
CLOCK_CONFIGURATION = PRECISION_USB_CLOCKING | TWAKEUP_64 ; //set up precision clocking, /64 wakeup prescaler
//this will set the initial wakeup time anywhere
//from 64 - 5*64 msec.
OpticsQueue.headP = bOpticsArray; //initialize queue pointers
OpticsQueue.tailP = bOpticsArray;
DeviceStatus.bProtocol = REPORT_PROTOCOL; //start in report protocol
USB_STATUS = VREG_ENABLE_MASK | NOT_FORCING;
PROCESSOR_STATUS &= ~(WATCHDOG_RESET_MASK //clear source of reset
| POWER_ON_RESET_MASK | USB_BUS_RESET_MASK);
GLOBAL_INTERRUPT = MILLISECOND_ENABLE //enable all pertinent interrupts
| BUS_RESET_ENABLE | MICROSECOND_ENABLE | WAKEUP_ENABLE;
USB_DEVICE_A = 0; //reset device address
EP_A0_MODE = USB_MODE_DISABLE; //disable endpoints from responding
EP_A1_MODE = USB_MODE_DISABLE;
EP_A2_MODE = USB_MODE_DISABLE;
ENDPOINT_INTERRUPT = (EPA0_ENABLE | EPA1_ENABLE); //turn on endpoint interrupts
return;
}
/*
**
** FUNCTION: MouseMoved
**
** PURPOSE: returns a 1 if the mouse's X,Y, or Z counts indicate movement.
**
** PARAMETERS: none
**
** DESCRIPTION: This routine will return a 1 if the accumulated X,Y, or Z counts are nonzero
** .
**
*/
char MouseMoved(void)
{
if (Mouse.bXcount || Mouse.bYcount)
return(1);
if((DeviceStatus.bProtocol == REPORT_PROTOCOL) //if Z wheel enabled, nonzero Z count requires a transmission
&& Mouse.bZcount)
return(1);
return(0);
}
/*
**
** FUNCTION: MouseTask
**
** PURPOSE: Handles mouse data transmission.
**
** PARAMETERS: none
**
** DESCRIPTION: This routine is called every 4 msec from the main loop. It
** maintains the idle counter, which determines the rate at which mouse packets
** are sent to the host in the absence of a state change in the mouse itself.
** It also sends a mouse packet if either of X,Y, or Z counts or the buttons have
** changed state.
**
*/
void MouseTask(void)
{
Mouse.bChange |= DebounceButtons(); //keep track of button changes
/*
** if the idle period is nonzero, and the decremented counter rolls to zero,
** set the change flag so we will send a packet regardless.
*/
if (MouseStatus.bIdlePeriod && !--MouseStatus.bIdleCounter)
{
MouseStatus.bIdleCounter = MouseStatus.bIdlePeriod;
Mouse.bChange = 1;
}
if ((EP_A1_MODE & USB_MODE_MASK) == USB_MODE_NAK_IN) //we are NAKing, so it's ok to transmit a new package
{
Mouse.bChange |= MouseMoved();
if (Mouse.bChange) //ok-transmission definitely required
{
ENDPOINT_A1_FIFO[0] = Mouse.bButtons; //load mouse data into fifo
ENDPOINT_A1_FIFO[1] = Mouse.bXcount;
Mouse.bXcount = 0;
ENDPOINT_A1_FIFO[2] = Mouse.bYcount;
Mouse.bYcount = 0;
ENDPOINT_A1_FIFO[3] = Mouse.bZcount;
Mouse.bZcount = 0;
if (DeviceStatus.bProtocol == REPORT_PROTOCOL) //if report protocol, include z counts in length
EP_A1_COUNTER = (EP_A1_COUNTER & DATATOGGLE) | 4;
else
EP_A1_COUNTER = (EP_A1_COUNTER & DATATOGGLE) | 3; //else omit it
EP_A1_MODE = USB_MODE_ACK_IN; //flip mode to ack the in with the data
MouseStatus.bIdleCounter = MouseStatus.bIdlePeriod; //and reset the idle period.
Mouse.bChange = 0; //clear the flag
}
}
}
/*
**
** FUNCTION: BusInactive
**
** PURPOSE: Tests for upstream activity
**
** PARAMETERS: none
**
** DESCRIPTION: This routine should be called every msec from the main loop.
** it maintains an internal count of the successive samples of the USB status register
** in which no bus activity was recorded. When this count exceeds 3 (msec), the
** routine returns 1, indicating that bus activity has deceased
**
*/
char BusInactive(void)
{
if (!(USB_STATUS & BUS_ACTIVITY_MASK)) //if no bus activity indicated in the status reg,
return(++bSuspendCounter == 3); //return 0 if the counter has elapsed
USB_STATUS = VREG_ENABLE_MASK; //reset the flag (always write bit 4 to a 0 in this reg)
bSuspendCounter = 0;
return(0);
}
/*
**
** FUNCTION: Suspend
**
** PURPOSE: puts the chip into suspend
**
** PARAMETERS: none
**
** DESCRIPTION:
** This routine handles the entrance/exit from suspend. If the mouse is configured for
** remote wakeup, the bus reset, wakeup, and gpio interrupts are enabled. The optical
** inputs are sampled once. The code then enters a loop in which the chip is suspended,
** and will wake at least as often as the wakeup ISR, but perhaps due to a GPIO or bus
** reset interrupt. Each time the chip wakes up, the led drive is reenabled, and the
** switches and optical inputs are sampled to see if a change occured, and bus activity
** is monitored. Any of these conditions will cause the firmware to exit the loop.
**
** if the device is not enabled for remote wakeup, all ports are put into the hi-Z state,
** only the bus reset interrupt is enabled,and the part is suspended.
**
** if the resume was due to bus activity, the firmware returns to the main loop.
** if the resume was due to mouse movement or a button press, a K state is driven upstream
** for 14 msec prior to returning to the main loop.
*/
void Suspend(void)
{
char Optics;
char temp = 10;
if (!DeviceStatus.bRemoteWakeup)
{
GLOBAL_INTERRUPT = BUS_RESET_ENABLE; //enable bus reset ISR only
PORT0_MODE0 = PORT0_MODE0_SUSPEND;
PORT1_MODE0 = PORT1_MODE0_SUSPEND;
PORT0_MODE1 = PORT0_MODE1_SUSPEND;
PORT1_MODE1 = PORT1_MODE1_SUSPEND;
PORT0 = PORT0_SUSPEND;
PORT1 = PORT1_SUSPEND;
PROCESSOR_STATUS |= 0x8; //suspend
#asm(nop);
RESET_COP();
PORT0_MODE0 = PORT0_MODE0_INIT; //re-init port modes
PORT1_MODE0 = PORT1_MODE0_INIT;
PORT0_MODE1 = PORT0_MODE1_INIT;
PORT1_MODE1 = PORT1_MODE1_INIT;
PORT0 = PORT0_INIT;
PORT1 = PORT1_INIT;
}
else
{
//enable gpio, wakeup, and bus reset ISRs
GLOBAL_INTERRUPT = BUS_RESET_ENABLE | GPIO_ENABLE | WAKEUP_ENABLE ;
Optics = OPTICS_PORT; //sample the optics port
while (1)
{
PORT0_MODE0 = PORT0_MODE0_RW; //set ports to remote wakeup configuration
PORT1_MODE0 = PORT1_MODE0_RW;
PORT0_MODE1 = PORT0_MODE1_RW;
PORT1_MODE1 = PORT1_MODE1_RW;
PORT0 = PORT0_RW;
PORT1 = PORT1_RW;
PROCESSOR_STATUS |= 0x8; //suspend (wakeup timer will wake us up)
#asm(nop);
PORT0_MODE0 = PORT0_MODE0_INIT; //re-init port modes
PORT1_MODE0 = PORT1_MODE0_INIT;
PORT0_MODE1 = PORT0_MODE1_INIT;
PORT1_MODE1 = PORT1_MODE1_INIT;
PORT0 = PORT0_INIT;
PORT1 = PORT1_INIT;
RESET_COP();
if (USB_STATUS & BUS_ACTIVITY_MASK) //if wakeup due to bus activity (including bus reset), get out
//of this loop
break;
if (GetButtons() != Mouse.bButtons) //if mouse buttons switches different, get out
break;
Delay(20); //wait some more time for optic led to come on
if ((OPTICS_PORT ^ Optics) & OPTICS_MASK) //if optics have changed, get out
break;
}
}
GLOBAL_INTERRUPT = MILLISECOND_ENABLE //reenable all interrupts used in main loop
| BUS_RESET_ENABLE | MICROSECOND_ENABLE | WAKEUP_ENABLE;
temp = MsecStatus.b1msCounter + 5; //wait 5 msec
while (temp != MsecStatus.b1msCounter)
{
RESET_COP();
if (USB_STATUS & BUS_ACTIVITY_MASK) //if bus activity is present, get out now
return;
}
USB_STATUS = VREG_ENABLE_MASK | FORCE_J; //otherwise force resume upstream for 14 msec
USB_STATUS = VREG_ENABLE_MASK | FORCE_K;
temp = MsecStatus.b1msCounter + 14; //
while (temp != MsecStatus.b1msCounter)
RESET_COP();
USB_STATUS = VREG_ENABLE_MASK | NOT_FORCING; //switch to nonforcing
bSuspendCounter = 0;
return;
}
/*
**
** FUNCTION: HandleIn
**
** PURPOSE: Services In packets
**
** PARAMETERS: none
**
** DESCRIPTION:
** This routine is entered whenever an IN packet has come in on endpoint 0.
** it processes the packet.
**
*/
void HandleIn(void)
{
//an ACKed IN occurs either in the case of the status phase of a control write,
//or the data in phase of a control read. The only thing we are expecting is the
//data in phase -- there are no control writes to the mouse
if ((EP_A0_MODE & USB_MODE_MASK) != USB_MODE_NAK_IN_STATUS_OUT )
{
SET_EP0_MODE(USB_MODE_STALL_IN_OUT); //we weren't expecting this!!
return;
}
byte_count = EP_A0_COUNTER; //unlock the counter register by reading it
byte_count = 0; //zero the byte count
if (XmtBuff.bLength) //if we've stuff to send,
byte_count = LoadEP0Fifo(); //load it into fifo
//account for the bytes we will send this pass
//load up the counter
EP_A0_COUNTER = (EP_A0_COUNTER & DATATOGGLE) ^ DATATOGGLE;
EP_A0_COUNTER |= byte_count;
//and set the ep mode to ack the next IN with the data.
EP_A0_MODE = USB_MODE_ACK_IN_STATUS_OUT;
}
/*
**
** FUNCTION: USB_control_read
**
** PURPOSE: Prepares the SIE for the first IN after a SETUP requesting data
**
** PARAMETERS: none
**
** DESCRIPTION:
** This routine is called after a SETUP has been received that is requesting
** a data response from the mouse. Upon entry, XmtBuff has been initialized to
** point to the data buffer that needs to be tranmsitted. USB_control_read
** adjusts the length of the data to be returned if the host requested less
** data than the actual length of the buffer. It then loads the FIFO with the
** first chunck of data and prepares the SIE to ACK with the data.
**
*/
void USB_control_read(void)
{
//if the host requested less data than is actually available, use that length instead
if ((!ENDPOINT_A0_FIFO[USB_wLengthHi]) && (ENDPOINT_A0_FIFO[USB_wLength] < XmtBuff.bLength))
XmtBuff.bLength = ENDPOINT_A0_FIFO[USB_wLength];
byte_count = 0;
if (XmtBuff.bLength)
byte_count = LoadEP0Fifo(); //data to send. Load FIFO with first chunck
EP_A0_COUNTER = DATATOGGLE | byte_count; //set up counter
EP_A0_MODE = USB_MODE_ACK_IN_STATUS_OUT; //and set up SIE to ACK with the data
}
/*
**
** FUNCTION: LoadEP0Fifo
**
** PURPOSE: loads EP0 fifo with available data
**
** PARAMETERS: bLength -- length of data available to load
**
** DESCRIPTION:
** This routine loads the fifo with data pointed to by XmtBuff. It
** returns the length of data actually loaded into the FIFO.
**
*/
char LoadEP0Fifo(void)
{
byte_count1 = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -