Pinscape Controller version 1 fork. This is a fork to allow for ongoing bug fixes to the original controller version, from before the major changes for the expansion board project.

Dependencies:   FastIO FastPWM SimpleDMA mbed

Fork of Pinscape_Controller by Mike R

Files at this revision

API Documentation at this revision

Comitter:
mjr
Date:
Thu Feb 11 20:24:50 2016 +0000
Parent:
55:e47a4b7ab348
Child:
57:20d54f25065a
Commit message:
Roll-forward #3 (superficial USBHAL_KL25Z changes)

Changed in this revision

USBDevice/USBDevice/USBHAL_KL25Z.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/USBDevice/USBDevice/USBHAL_KL25Z.cpp	Thu Feb 11 19:14:12 2016 +0000
+++ b/USBDevice/USBDevice/USBHAL_KL25Z.cpp	Thu Feb 11 20:24:50 2016 +0000
@@ -18,42 +18,90 @@
 
 #if defined(TARGET_KL25Z) | defined(TARGET_KL46Z) | defined(TARGET_K20D5M) | defined(TARGET_K64F)
 
+//#define DEBUG
+#ifdef DEBUG
+#define printd(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define printd(fmt, ...)
+#endif
+
 #include "USBHAL.h"
 
+// Critical section controls.  This module uses a bunch of static variables,
+// and much of the code that accesses the statics can be called from either
+// normal application context or IRQ context.  Whenever a shared variable is
+// accessed from code that can run in an application context, we have to
+// protect against interrupts by entering a critical section.  These macros
+// enable and disable the USB IRQ if we're running in application context.
+// (They do nothing if we're already in interrupt context, because the
+// hardware interrupt controller won't generated another of the same IRQ
+// that we're already handling.  We could still be interrupted by a different,
+// higher-priority IRQ, but our shared variables are only shared within this
+// module, so they won't be affected by other interrupt handlers.)
+static int inIRQ;
+#define ENTER_CRITICAL_SECTION \
+    if (!inIRQ) \
+        NVIC_DisableIRQ(USB0_IRQn);
+#define EXIT_CRITICAL_SECTION \
+    if (!inIRQ) \
+        NVIC_EnableIRQ(USB0_IRQn);
+
+// static singleton instance pointer
 USBHAL * USBHAL::instance;
 
-static volatile int epComplete = 0;
 
 // Convert physical endpoint number to register bit
 #define EP(endpoint) (1<<(endpoint))
 
-// Convert physical to logical
+// Convert physical endpoint number to logical endpoint number.
+// Each logical endpoint has two physical endpoints, one RX and 
+// one TX.  The physical endpoints are numbered in RX,TX pairs,
+// so the logical endpoint number is simply the physical endpoint
+// number divided by 2 (discarding the remainder).
 #define PHY_TO_LOG(endpoint)    ((endpoint)>>1)
 
-// Get endpoint direction
+// Get a physical endpoint's direction.  IN and OUT are from
+// the host's perspective, so from our perspective on the device,
+// IN == TX and OUT == RX.  The physical endpoints are in RX,TX
+// pairs, so the OUT/RX is the even numbered element of a pair
+// and the IN/TX is the odd numbered element.
 #define IN_EP(endpoint)     ((endpoint) & 1U ? true : false)
 #define OUT_EP(endpoint)    ((endpoint) & 1U ? false : true)
 
-#define BD_OWN_MASK        (1<<7)
-#define BD_DATA01_MASK     (1<<6)
-#define BD_KEEP_MASK       (1<<5)
-#define BD_NINC_MASK       (1<<4)
-#define BD_DTS_MASK        (1<<3)
-#define BD_STALL_MASK      (1<<2)
+// BDT status flags, defined by the SIE hardware.  These are
+// bits packed into the 'info' byte of a BDT entry.
+#define BD_OWN_MASK        (1<<7)       // OWN - hardware SIE owns the BDT (TX/RX in progress)
+#define BD_DATA01_MASK     (1<<6)       // DATA01 - DATA0/DATA1 bit for current TX/RX on endpoint
+#define BD_KEEP_MASK       (1<<5)       // KEEP - hardware keeps BDT ownership after token completes
+#define BD_NINC_MASK       (1<<4)       // NO INCREMENT - buffer location is a FIFO, so use same address for all bytes
+#define BD_DTS_MASK        (1<<3)       // DATA TOGGLE SENSING - hardware SIE checks for DATA0/DATA1 match during RX/TX
+#define BD_STALL_MASK      (1<<2)       // STALL - SIE issues STALL handshake in reply to any host access to endpoint
 
+// Endpoint direction (from DEVICE perspective)
 #define TX    1
 #define RX    0
-#define ODD   0
-#define EVEN  1
-// this macro waits a physical endpoint number
-#define EP_BDT_IDX(ep, dir, odd) (((ep * 4) + (2 * dir) + (1 *  odd)))
+
+// Buffer parity.  The hardware has a double-buffering scheme where each
+// physical endpoint has two associated BDT entries, labeled EVEN and ODD.
+// We disable the double buffering, so only the EVEN buffers are used in
+// this implementation.
+#define EVEN  0
+#define ODD   1
 
+// Get the BDT index for a given logical endpoint, direction, and buffer parity
+#define EP_BDT_IDX(logep, dir, odd) ((((logep) * 4) + (2 * (dir)) + (1 *  (odd))))
+
+// Get the BDT index for a given physical endpoint and buffer parity
+#define PEP_BDT_IDX(phyep, odd)  (((phyep) * 2) + (1 * (odd)))
+
+// Token types reported in the BDT 'info' flags.  
+#define TOK_PID(idx)   ((bdt[idx].info >> 2) & 0x0F)
 #define SETUP_TOKEN    0x0D
 #define IN_TOKEN       0x09
 #define OUT_TOKEN      0x01
-#define TOK_PID(idx)   ((bdt[idx].info >> 2) & 0x0F)
 
-// for each endpt: 8 bytes
+// Buffer Descriptor Table (BDT) entry.  This is the hardware-defined
+// memory structure for the shared memory block controlling an endpoint.
 typedef struct BDT {
     uint8_t   info;       // BD[0:7]
     uint8_t   dummy;      // RSVD: BD[8:15]
@@ -62,18 +110,37 @@
 } BDT;
 
 
-// there are:
-//    * 16 bidirectionnal endpt -> 32 physical endpt
-//    * as there are ODD and EVEN buffer -> 32*2 bdt
+// There are:
+//    * 16 bidirectional logical endpoints -> 32 physical endpoints
+//    * 2 BDT entries per endpoint (EVEN/ODD) -> 64 BDT entries
 __attribute__((__aligned__(512))) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
 uint8_t * endpoint_buffer[(NUMBER_OF_PHYSICAL_ENDPOINTS - 2) * 2];
 uint8_t * endpoint_buffer_iso[2*2];
 
+// SET ADDRESS mode tracking.  The address assignment has to be done in a
+// specific order and with specific timing defined by the USB setup protocol 
+// standards.  To get the sequencing right, we set a flag when we get the
+// address message, and then set the address in the SIE when we're at the 
+// right subsequent packet step in the protocol exchange.  These variables
+// are just a place to stash the information between the time we receive the
+// data and the time we're ready to update the SIE register.
 static uint8_t set_addr = 0;
 static uint8_t addr = 0;
 
+// Endpoint DATA0/DATA1 bits, packed as a bit vector.  Each endpoint's
+// bit is at (1 << endpoint number).  These track the current bit value
+// on the endpoint.  For TX endpoints, this is the bit for the LAST
+// packet we sent (so the next packet will be the inverse).  For RX
+// endpoints, this is the bit value we expect for the NEXT packet.
+// (Yes, it's inconsistent.)
 static uint32_t Data1  = 0x55555555;
 
+// Endpoint read/write completion flags, packed as a bit vector.  Each 
+// endpoint's bit is at (1 << endpoint number).  A 1 bit signifies that
+// the last read or write has completed (and hasn't had its result 
+// consumed yet).
+static volatile int epComplete = 0;
+
 static uint32_t frameNumber() {
     return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
 }
@@ -215,13 +282,13 @@
     if ((flags & ISOCHRONOUS) == 0) {
         handshake_flag = USB_ENDPT_EPHSHK_MASK;
         if (IN_EP(endpoint)) {
-            if (endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] == NULL)
-                endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] = (uint8_t *) malloc (64*2);
-            buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)][0];
+            if (endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, EVEN)] == NULL)
+                endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, EVEN)] = (uint8_t *) malloc (64*2);
+            buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, EVEN)][0];
         } else {
-            if (endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] == NULL)
-                endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] = (uint8_t *) malloc (64*2);
-            buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
+            if (endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, EVEN)] == NULL)
+                endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, EVEN)] = (uint8_t *) malloc (64*2);
+            buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, EVEN)][0];
         }
     } else {
         if (IN_EP(endpoint)) {
@@ -239,17 +306,17 @@
     if (IN_EP(endpoint)) {
         USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag |        // ep handshaking (not if iso endpoint)
                                               USB_ENDPT_EPTXEN_MASK;  // en TX (IN) tran
-        bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = (uint32_t) buf;
-        bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
+        bdt[EP_BDT_IDX(log_endpoint, TX, EVEN )].address = (uint32_t) buf;
+        bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = 0;
     }
     // OUT endpt -> host to device (RX)
     else {
         USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag |        // ep handshaking (not if iso endpoint)
                                               USB_ENDPT_EPRXEN_MASK;  // en RX (OUT) tran.
-        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].byte_count = maxPacket;
-        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address    = (uint32_t) buf;
-        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info       = BD_OWN_MASK | BD_DTS_MASK;
-        bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info       = 0;
+        bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].byte_count = maxPacket;
+        bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].address    = (uint32_t) buf;
+        bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info       = BD_OWN_MASK | BD_DTS_MASK;
+        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info       = 0;
     }
 
     Data1 |= (1 << endpoint);