Add USB code
Adds the usb code that we got in ECE 385. It will not work now, and parts that involve the timer are disabled. It does compile though, with a few warnings. The goal will be to add USB MSD support, which is not actually given to us so I will have to do myself or find some other code to base it off of.
This commit is contained in:
250
sw/usb/MAX3421E.c
Normal file
250
sw/usb/MAX3421E.c
Normal file
@@ -0,0 +1,250 @@
|
||||
//Fill in your low-level SPI functions here, as per your host platform
|
||||
|
||||
#define _MAX3421E_C_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "project_config.h"
|
||||
#include <spi.h>
|
||||
#include <gpio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//variables and data structures
|
||||
//External variables
|
||||
extern BYTE usb_task_state;
|
||||
uint8_t* usb_gpio = USB_GPIO;
|
||||
|
||||
/* Functions */
|
||||
void SPI_init(BYTE sync_mode, BYTE bus_mode, BYTE smp_phase) {
|
||||
//Don't need to initialize SPI port, already ready to go with BSP
|
||||
(void) sync_mode;
|
||||
(void) bus_mode;
|
||||
(void) smp_phase;
|
||||
}
|
||||
|
||||
//writes single byte to MAX3421E via SPI, simultanously reads status register and returns it
|
||||
BYTE SPI_wr(BYTE data) {
|
||||
(void) data;
|
||||
//This function is never used by the code, so you do not need to fill it in
|
||||
return -1;
|
||||
}
|
||||
//writes register to MAX3421E via SPI
|
||||
void MAXreg_wr(BYTE reg, BYTE val) {
|
||||
//psuedocode:
|
||||
//select MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//write reg + 2 via SPI
|
||||
//write val via SPI
|
||||
//read return code from SPI peripheral (see Intel documentation)
|
||||
//if return code < 0 print an error
|
||||
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
BYTE regdir = reg + 2; //reg bitshift plus direction bit.
|
||||
|
||||
spi_byte(regdir);
|
||||
spi_byte(val);
|
||||
spi_deselect();
|
||||
}
|
||||
//multiple-byte write
|
||||
//returns a pointer to a memory position after last written
|
||||
BYTE* MAXbytes_wr(BYTE reg, BYTE nbytes, BYTE* data) {
|
||||
//psuedocode:
|
||||
//select MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//write reg + 2 via SPI
|
||||
//write data[n] via SPI, where n goes from 0 to nbytes-1
|
||||
//read return code from SPI peripheral (see Intel documentation)
|
||||
//if return code < 0 print an error
|
||||
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//return (data + nbytes);
|
||||
int i;
|
||||
BYTE regdir = reg + 2;
|
||||
|
||||
spi_byte(regdir);
|
||||
|
||||
for (i = 0; i < nbytes; i++){
|
||||
spi_byte(data[i]);
|
||||
}
|
||||
|
||||
spi_deselect();
|
||||
|
||||
return data + nbytes;
|
||||
}
|
||||
|
||||
//reads register from MAX3421E via SPI
|
||||
BYTE MAXreg_rd(BYTE reg) {
|
||||
//psuedocode:
|
||||
//select MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//write reg via SPI
|
||||
//read val via SPI
|
||||
//read return code from SPI peripheral (see Intel documentation)
|
||||
//if return code < 0 print an error
|
||||
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//return val
|
||||
BYTE regdir = reg;
|
||||
BYTE rxdata;
|
||||
|
||||
spi_byte(regdir);
|
||||
rxdata = spi_byte(0);
|
||||
spi_deselect();
|
||||
|
||||
return rxdata;
|
||||
}
|
||||
//multiple-byte write
|
||||
//returns a pointer to a memory position after last written
|
||||
BYTE* MAXbytes_rd(BYTE reg, BYTE nbytes, BYTE* data) {
|
||||
//psuedocode:
|
||||
//select MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//write reg via SPI
|
||||
//read data[n] from SPI, where n goes from 0 to nbytes-1
|
||||
//read return code from SPI peripheral (see Intel documentation)
|
||||
//if return code < 0 print an error
|
||||
//deselect MAX3421E (may not be necessary if you are using SPI peripheral)
|
||||
//return (data + nbytes);
|
||||
int i;
|
||||
BYTE regdir = reg;
|
||||
|
||||
spi_byte(regdir);
|
||||
|
||||
for (i = 0; i < nbytes; i++){
|
||||
data[i] = spi_byte(0);
|
||||
}
|
||||
|
||||
spi_deselect();
|
||||
|
||||
return data + nbytes;
|
||||
}
|
||||
/* reset MAX3421E using chip reset bit. SPI configuration is not affected */
|
||||
void MAX3421E_reset(void) {
|
||||
//hardware reset, then software reset
|
||||
BYTE tmp;
|
||||
|
||||
*usb_gpio &= ~USB_GPIO_RST;
|
||||
//TODO
|
||||
//sleep(1);
|
||||
*usb_gpio |= USB_GPIO_RST;
|
||||
tmp = 0;
|
||||
MAXreg_wr( rUSBCTL, bmCHIPRES); //Chip reset. This stops the oscillator
|
||||
MAXreg_wr( rUSBCTL, 0x00); //Remove the reset
|
||||
while (!(MAXreg_rd( rUSBIRQ) & bmOSCOKIRQ)) { //wait until the PLL stabilizes
|
||||
tmp++; //timeout after 256 attempts
|
||||
if (tmp == 0) {
|
||||
//printf("reset timeout!");
|
||||
}
|
||||
}
|
||||
}
|
||||
/* turn USB power on/off */
|
||||
/* ON pin of VBUS switch (MAX4793 or similar) is connected to GPOUT7 */
|
||||
/* OVERLOAD pin of Vbus switch is connected to GPIN7 */
|
||||
/* OVERLOAD state low. NO OVERLOAD or VBUS OFF state high. */
|
||||
BOOL Vbus_power(BOOL action) {
|
||||
// power on/off successful
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* probe bus to determine device presense and speed */
|
||||
void MAX_busprobe(void) {
|
||||
BYTE bus_sample;
|
||||
|
||||
// MAXreg_wr(rHCTL,bmSAMPLEBUS);
|
||||
bus_sample = MAXreg_rd( rHRSL); //Get J,K status
|
||||
bus_sample &= ( bmJSTATUS | bmKSTATUS); //zero the rest of the byte
|
||||
|
||||
switch (bus_sample) { //start full-speed or low-speed host
|
||||
case ( bmJSTATUS):
|
||||
/*kludgy*/
|
||||
if (usb_task_state != USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE) { //bus reset causes connection detect interrupt
|
||||
if (!(MAXreg_rd( rMODE) & bmLOWSPEED)) {
|
||||
MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host
|
||||
//printf("Starting in full speed\n");
|
||||
} else {
|
||||
MAXreg_wr( rMODE, MODE_LS_HOST); //start low-speed host
|
||||
//printf("Starting in low speed\n");
|
||||
}
|
||||
usb_task_state = ( USB_STATE_ATTACHED); //signal usb state machine to start attachment sequence
|
||||
}
|
||||
break;
|
||||
case ( bmKSTATUS):
|
||||
if (usb_task_state != USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE) { //bus reset causes connection detect interrupt
|
||||
if (!(MAXreg_rd( rMODE) & bmLOWSPEED)) {
|
||||
MAXreg_wr( rMODE, MODE_LS_HOST); //start low-speed host
|
||||
//printf("Starting in low speed\n");
|
||||
} else {
|
||||
MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host
|
||||
//printf("Starting in full speed\n");
|
||||
}
|
||||
usb_task_state = ( USB_STATE_ATTACHED); //signal usb state machine to start attachment sequence
|
||||
}
|
||||
break;
|
||||
case ( bmSE1): //illegal state
|
||||
usb_task_state = ( USB_DETACHED_SUBSTATE_ILLEGAL);
|
||||
break;
|
||||
case ( bmSE0): //disconnected state
|
||||
if (!((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED)) //if we came here from other than detached state
|
||||
usb_task_state = ( USB_DETACHED_SUBSTATE_INITIALIZE); //clear device data structures
|
||||
else {
|
||||
MAXreg_wr( rMODE, MODE_FS_HOST); //start full-speed host
|
||||
usb_task_state = ( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE);
|
||||
}
|
||||
break;
|
||||
} //end switch( bus_sample )
|
||||
}
|
||||
/* MAX3421E initialization after power-on */
|
||||
void MAX3421E_init(void) {
|
||||
/* Configure full-duplex SPI, interrupt pulse */
|
||||
MAXreg_wr( rPINCTL, (bmFDUPSPI + bmINTLEVEL + bmGPXB)); //Full-duplex SPI, level interrupt, GPX
|
||||
MAX3421E_reset(); //stop/start the oscillator
|
||||
/* configure power switch */
|
||||
Vbus_power( OFF); //turn Vbus power off
|
||||
MAXreg_wr( rGPINIEN, bmGPINIEN7); //enable interrupt on GPIN7 (power switch overload flag)
|
||||
Vbus_power( ON);
|
||||
/* configure host operation */
|
||||
MAXreg_wr( rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ); // set pull-downs, SOF, Host, Separate GPIN IRQ on GPX
|
||||
//MAXreg_wr( rHIEN, bmFRAMEIE|bmCONDETIE|bmBUSEVENTIE ); // enable SOF, connection detection, bus event IRQs
|
||||
MAXreg_wr( rHIEN, bmCONDETIE); //connection detection
|
||||
/* HXFRDNIRQ is checked in Dispatch packet function */
|
||||
MAXreg_wr(rHCTL, bmSAMPLEBUS); // update the JSTATUS and KSTATUS bits
|
||||
MAX_busprobe(); //check if anything is connected
|
||||
MAXreg_wr( rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
|
||||
MAXreg_wr( rCPUCTL, 0x01); //enable interrupt pin
|
||||
}
|
||||
|
||||
/* MAX3421 state change task and interrupt handler */
|
||||
void MAX3421E_Task(void) {
|
||||
if ((*usb_gpio & USB_GPIO_IRQ) == 0) {
|
||||
//printf("MAX interrupt\n\r");
|
||||
MaxIntHandler();
|
||||
}
|
||||
if ((*usb_gpio & USB_GPIO_GPX) != 0) {
|
||||
//printf("GPX interrupt\n\r");
|
||||
MaxGpxHandler();
|
||||
}
|
||||
}
|
||||
|
||||
void MaxIntHandler(void) {
|
||||
BYTE HIRQ;
|
||||
BYTE HIRQ_sendback = 0x00;
|
||||
HIRQ = MAXreg_rd( rHIRQ); //determine interrupt source
|
||||
//printf("IRQ: %x\n", HIRQ);
|
||||
if (HIRQ & bmFRAMEIRQ) { //->1ms SOF interrupt handler
|
||||
HIRQ_sendback |= bmFRAMEIRQ;
|
||||
} //end FRAMEIRQ handling
|
||||
|
||||
if (HIRQ & bmCONDETIRQ) {
|
||||
MAX_busprobe();
|
||||
HIRQ_sendback |= bmCONDETIRQ; //set sendback to 1 to clear register
|
||||
}
|
||||
if (HIRQ & bmSNDBAVIRQ) //if the send buffer is clear (previous transfer completed without issue)
|
||||
{
|
||||
MAXreg_wr(rSNDBC, 0x00);//clear the send buffer (not really necessary, but clears interrupt)
|
||||
}
|
||||
if (HIRQ & bmBUSEVENTIRQ) { //bus event is either reset or suspend
|
||||
usb_task_state++; //advance USB task state machine
|
||||
HIRQ_sendback |= bmBUSEVENTIRQ;
|
||||
}
|
||||
/* End HIRQ interrupts handling, clear serviced IRQs */
|
||||
MAXreg_wr( rHIRQ, HIRQ_sendback); //write '1' to CONDETIRQ to ack bus state change
|
||||
}
|
||||
|
||||
void MaxGpxHandler(void) {
|
||||
BYTE GPINIRQ;
|
||||
GPINIRQ = MAXreg_rd( rGPINIRQ); //read both IRQ registers
|
||||
}
|
||||
Reference in New Issue
Block a user