浏览代码

STM32F7: Floppy Control

Keir Fraser 5 年之前
父节点
当前提交
9a1702abd3
共有 12 个文件被更改,包括 434 次插入208 次删除
  1. 1 0
      inc/stm32/f1.h
  2. 24 24
      inc/stm32/f1_regs.h
  3. 1 0
      inc/stm32/f7.h
  4. 16 12
      inc/stm32/f7_regs.h
  5. 1 1
      src/Makefile
  6. 13 13
      src/blinky.c
  7. 2 2
      src/console.c
  8. 77 143
      src/floppy.c
  9. 148 0
      src/floppy_f1.c
  10. 151 0
      src/floppy_f7.c
  11. 0 3
      src/main.c
  12. 0 10
      src/stm32f7.c

+ 1 - 0
inc/stm32/f1.h

@@ -12,6 +12,7 @@
 /* C pointer types */
 #define BKP volatile struct bkp * const
 #define AFIO volatile struct afio * const
+#define DMA_CHN volatile struct dma_chn * const
 
 /* C-accessible registers. */
 static STK stk = (struct stk *)STK_BASE;

+ 24 - 24
inc/stm32/f1_regs.h

@@ -243,10 +243,10 @@ struct afio {
 
 /* DMA */
 struct dma_chn {
-    uint32_t ccr;        /* +00: Configuration */
-    uint32_t cndtr;      /* +04: Number of data */
-    uint32_t cpar;       /* +08: Peripheral address */
-    uint32_t cmar;       /* +0C: Memory address */
+    uint32_t cr;         /* +00: Configuration */
+    uint32_t ndtr;       /* +04: Number of data */
+    uint32_t par;        /* +08: Peripheral address */
+    uint32_t mar;        /* +0C: Memory address */
     uint32_t rsvd;       /* +10: - */
 };
 struct dma {
@@ -273,26 +273,26 @@ struct dma {
 #define DMA_IFCR_CTCIF(n)    (2u<<(((n)-1)*4))
 #define DMA_IFCR_CGIF(n)     (1u<<(((n)-1)*4))
 
-#define DMA_CCR_MEM2MEM      (1u<<14)
-#define DMA_CCR_PL_LOW       (0u<<12)
-#define DMA_CCR_PL_MEDIUM    (1u<<12)
-#define DMA_CCR_PL_HIGH      (2u<<12)
-#define DMA_CCR_PL_V_HIGH    (3u<<12)
-#define DMA_CCR_MSIZE_8BIT   (0u<<10)
-#define DMA_CCR_MSIZE_16BIT  (1u<<10)
-#define DMA_CCR_MSIZE_32BIT  (2u<<10)
-#define DMA_CCR_PSIZE_8BIT   (0u<< 8)
-#define DMA_CCR_PSIZE_16BIT  (1u<< 8)
-#define DMA_CCR_PSIZE_32BIT  (2u<< 8)
-#define DMA_CCR_MINC         (1u<< 7)
-#define DMA_CCR_PINC         (1u<< 6)
-#define DMA_CCR_CIRC         (1u<< 5)
-#define DMA_CCR_DIR_P2M      (0u<< 4)
-#define DMA_CCR_DIR_M2P      (1u<< 4)
-#define DMA_CCR_TEIE         (1u<< 3)
-#define DMA_CCR_HTIE         (1u<< 2)
-#define DMA_CCR_TCIE         (1u<< 1)
-#define DMA_CCR_EN           (1u<< 0)
+#define DMA_CR_MEM2MEM       (1u<<14)
+#define DMA_CR_PL_LOW        (0u<<12)
+#define DMA_CR_PL_MEDIUM     (1u<<12)
+#define DMA_CR_PL_HIGH       (2u<<12)
+#define DMA_CR_PL_V_HIGH     (3u<<12)
+#define DMA_CR_MSIZE_8BIT    (0u<<10)
+#define DMA_CR_MSIZE_16BIT   (1u<<10)
+#define DMA_CR_MSIZE_32BIT   (2u<<10)
+#define DMA_CR_PSIZE_8BIT    (0u<< 8)
+#define DMA_CR_PSIZE_16BIT   (1u<< 8)
+#define DMA_CR_PSIZE_32BIT   (2u<< 8)
+#define DMA_CR_MINC          (1u<< 7)
+#define DMA_CR_PINC          (1u<< 6)
+#define DMA_CR_CIRC          (1u<< 5)
+#define DMA_CR_DIR_P2M       (0u<< 4)
+#define DMA_CR_DIR_M2P       (1u<< 4)
+#define DMA_CR_TEIE          (1u<< 3)
+#define DMA_CR_HTIE          (1u<< 2)
+#define DMA_CR_TCIE          (1u<< 1)
+#define DMA_CR_EN            (1u<< 0)
 
 #define DMA1_BASE 0x40020000
 #define DMA2_BASE 0x40020400

+ 1 - 0
inc/stm32/f7.h

@@ -11,6 +11,7 @@
 
 /* C pointer types */
 #define SYSCFG volatile struct syscfg * const
+#define DMA_STR volatile struct dma_str * const
 
 /* C-accessible registers. */
 static STK stk = (struct stk *)STK_BASE;

+ 16 - 12
inc/stm32/f7_regs.h

@@ -255,22 +255,23 @@ struct gpio {
 
 /* 0-1: MODE, 2: OTYPE, 3-4:OSPEED, 5-6:PUPD, 7:OUTPUT_LEVEL */
 #define GPI_analog    0x3u
-#define GPI_floating  0x0u
-#define _GPI_pulled(level) (0x0u|(((level)?1:2)<<5))
-#define GPI_pull_down _GPI_pulled(LOW)
-#define GPI_pull_up   _GPI_pulled(HIGH)
+#define GPI(pupd)     (0x0u|((pupd)<<5))
+#define PUPD_none     0
+#define PUPD_up       1
+#define PUPD_down     2
+#define GPI_floating  GPI(PUPD_none)
+#define GPI_pull_down GPI(PUPD_down)
+#define GPI_pull_up   GPI(PUPD_up)
 
 #define GPO_pushpull(speed,level)  (0x1u|((speed)<<3)|((level)<<7))
 #define GPO_opendrain(speed,level) (0x5u|((speed)<<3)|((level)<<7))
+#define AFI(pupd)                  (0x2u|((pupd)<<5))
 #define AFO_pushpull(speed)        (0x2u|((speed)<<3))
 #define AFO_opendrain(speed)       (0x6u|((speed)<<3))
-#define _4MHz   0 /* CL=50pF */
-#define _25MHz  1 /* CL=50pF */
-#define _50MHz  2 /* CL=40pF */
-#define _100MHz 3 /* CL=30pF */
-/* Compat defines */
-#define _2MHz  _4MHz
-#define _10MHz _25MHz
+#define IOSPD_LOW    0 /*   4MHz @ CL=50pF */
+#define IOSPD_MED    1 /*  25MHz @ CL=50pF */
+#define IOSPD_HIGH   2 /*  50MHz @ CL=40pF */
+#define IOSPD_V_HIGH 3 /* 100MHz @ CL=30pF */
 #define LOW  0
 #define HIGH 1
 
@@ -305,7 +306,10 @@ struct dma_str {
     uint32_t cr;        /* +00: Configuration */
     uint32_t ndtr;      /* +04: Number of data */
     uint32_t par;       /* +08: Peripheral address */
-    uint32_t m0ar;      /* +0C: Memory 0 address */
+    union {
+        uint32_t mar;   /* +0C: Memory address */
+        uint32_t m0ar;  /* +0C: Memory 0 address */
+    };
     uint32_t m1ar;      /* +10: Memory 1 address */
     uint32_t fcr;       /* +14: FIFO control */
 };

+ 1 - 1
src/Makefile

@@ -8,7 +8,7 @@ OBJS += stm32$(stm32).o
 OBJS += time.o
 OBJS += timer.o
 OBJS += util.o
-OBJS-$(stm32f1) += floppy.o
+OBJS += floppy.o
 
 OBJS-$(debug) += console.o
 

+ 13 - 13
src/blinky.c

@@ -94,7 +94,7 @@ static volatile int dmac;
 static void IRQ_dma_tc(void)
 {
     dma1->ifcr = DMA_IFCR_CGIF(1);
-    dma1->ch1.ccr = 0;
+    dma1->ch1.cr = 0;
     dmac++;
 }
 
@@ -159,20 +159,20 @@ static void dma_test(void *a, void *b, int nr)
      * quickly. */
     printk("DMA Test #%u... ", nr);
     dma1->ifcr = DMA_IFCR_CGIF(1);
-    dma1->ch1.ccr = 0;
-    dma1->ch1.cmar = (uint32_t)(unsigned long)a;
-    dma1->ch1.cpar = (uint32_t)(unsigned long)b;
-    dma1->ch1.cndtr = 1024;
+    dma1->ch1.cr = 0;
+    dma1->ch1.mar = (uint32_t)(unsigned long)a;
+    dma1->ch1.par = (uint32_t)(unsigned long)b;
+    dma1->ch1.ndtr = 1024;
     memset(b, 0x12, 1024); /* scratch the destination */
     dmac = 0;
-    dma1->ch1.ccr = (DMA_CCR_MSIZE_8BIT |
-                     DMA_CCR_PSIZE_8BIT |
-                     DMA_CCR_MINC |
-                     DMA_CCR_PINC |
-                     DMA_CCR_DIR_M2P |
-                     DMA_CCR_MEM2MEM |
-                     DMA_CCR_TCIE |
-                     DMA_CCR_EN);
+    dma1->ch1.cr = (DMA_CR_MSIZE_8BIT |
+                    DMA_CR_PSIZE_8BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_PINC |
+                    DMA_CR_DIR_M2P |
+                    DMA_CR_MEM2MEM |
+                    DMA_CR_TCIE |
+                    DMA_CR_EN);
     while (!dmac)
         continue;
     if (dmac > 1)

+ 2 - 2
src/console.c

@@ -89,8 +89,8 @@ void console_init(void)
 #elif STM32F == 7
     gpio_set_af(gpioa, 9, 7);
     gpio_set_af(gpioa, 10, 7);
-    gpio_configure_pin(gpioa, 9, AFO_pushpull(_10MHz));
-    gpio_configure_pin(gpioa, 10, AFO_pushpull(_10MHz));
+    gpio_configure_pin(gpioa, 9, AFO_pushpull(IOSPD_MED));
+    gpio_configure_pin(gpioa, 10, AFI(PUPD_up));
 #endif
 
     /* BAUD, 8n1. */

+ 77 - 143
src/floppy.c

@@ -9,38 +9,26 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-#define O_FALSE 1
-#define O_TRUE  0
+#define m(bitnr) (1u<<(bitnr))
 
-#define GPO_bus GPO_opendrain(_2MHz,O_FALSE)
-#define AFO_bus (AFO_opendrain(_2MHz) | (O_FALSE<<4))
+#define get_index()   gpio_read_pin(gpio_index, pin_index)
+#define get_trk0()    gpio_read_pin(gpio_trk0, pin_trk0)
+#define get_wrprot()  gpio_read_pin(gpio_wrprot, pin_wrprot)
 
-#define m(bitnr) (1u<<(bitnr))
+#define configure_pin(pin, type) \
+    gpio_configure_pin(gpio_##pin, pin_##pin, type)
 
-#define gpio_floppy gpiob
-
-/* Input pins */
-#define pin_index   6 /* PB6 */
-#define pin_trk0    7 /* PB7 */
-#define pin_wrprot  8 /* PB8 */
-#define get_index()   gpio_read_pin(gpio_floppy, pin_index)
-#define get_trk0()    gpio_read_pin(gpio_floppy, pin_trk0)
-#define get_wrprot()  gpio_read_pin(gpio_floppy, pin_wrprot)
-
-/* Output pins. */
-#define pin_densel  9 /* PB9 */
-#define pin_sel0   10 /* PB10 */
-#define pin_motor  11 /* PB11 */
-#define pin_dir    12 /* PB12 */
-#define pin_step   13 /* PB13 */
-#define pin_wgate  14 /* PB14 */
-#define pin_side   15 /* PB15 */
+#if STM32F == 1
+#include "floppy_f1.c"
+#elif STM32F == 7
+#include "floppy_f7.c"
+#endif
 
 /* Track and modify states of output pins. */
 static struct {
     bool_t densel;
     bool_t sel0;
-    bool_t motor;
+    bool_t mot0;
     bool_t dir;
     bool_t step;
     bool_t wgate;
@@ -48,21 +36,9 @@ static struct {
 } pins;
 #define read_pin(pin) pins.pin
 #define write_pin(pin, level) ({                                        \
-    gpio_write_pin(gpio_floppy, pin_##pin, level ? O_TRUE : O_FALSE);   \
+    gpio_write_pin(gpio_##pin, pin_##pin, level ? O_TRUE : O_FALSE);    \
     pins.pin = level; })
 
-#define gpio_data gpiob
-
-#define pin_rdata   3
-#define tim_rdata   (tim2)
-#define dma_rdata   (dma1->ch7)
-
-#define pin_wdata   4
-#define tim_wdata   (tim3)
-#define dma_wdata   (dma1->ch3)
-
-#define irq_index 23
-void IRQ_23(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI9_5 */
 static struct index {
     /* Main code can reset this at will. */
     volatile unsigned int count;
@@ -74,8 +50,6 @@ static struct index {
     time_t timestamp;
 } index;
 
-#define irq_index_delay 31
-void IRQ_31(void) __attribute__((alias("IRQ_INDEX_delay")));
 static void index_delay_timer(void *unused);
 
 /* A DMA buffer for running a timer associated with a floppy-data I/O pin. */
@@ -84,10 +58,10 @@ static struct dma_ring {
     uint16_t cons; /* dma_rd: our consumer index for flux samples */
     union {
         uint16_t prod; /* dma_wr: our producer index for flux samples */
-        uint16_t prev_sample; /* dma_rd: previous CCRx sample value */
+        timcnt_t prev_sample; /* dma_rd: previous CCRx sample value */
     };
     /* DMA ring buffer of timer values (ARR or CCRx). */
-    uint16_t buf[512];
+    timcnt_t buf[512];
 } dma;
 
 static struct {
@@ -192,72 +166,69 @@ static bool_t floppy_seek(unsigned int cyl)
 
 static void drive_motor(bool_t on)
 {
-    if (read_pin(motor) == on)
+    if (read_pin(mot0) == on)
         return;
-    write_pin(motor, on);
+    write_pin(mot0, on);
     if (on)
         delay_ms(delay_params.motor_delay);
 }
 
 static void floppy_flux_end(void)
 {
+    /* Turn off write pins. */
+    write_pin(wgate, FALSE);
+    configure_pin(wdata, GPO_bus);    
+
+    /* Turn off DMA. */
+    dma_rdata.cr &= ~DMA_CR_EN;
+    dma_wdata.cr &= ~DMA_CR_EN;
+    while ((dma_rdata.cr & DMA_CR_EN) || (dma_wdata.cr & DMA_CR_EN))
+        continue;
+
     /* Turn off timers. */
     tim_rdata->ccer = 0;
     tim_rdata->cr1 = 0;
     tim_rdata->sr = 0; /* dummy, drains any pending DMA */
+    tim_wdata->ccer = 0;
     tim_wdata->cr1 = 0;
     tim_wdata->sr = 0; /* dummy, drains any pending DMA */
-
-    /* Turn off DMA. */
-    dma_rdata.ccr = 0;
-    dma_wdata.ccr = 0;
-
-    /* Turn off write pins. */
-    write_pin(wgate, FALSE);
-    gpio_configure_pin(gpio_data, pin_wdata, GPO_bus);    
 }
 
 static void floppy_reset(void)
 {
-    unsigned int i;
-
     floppy_state = ST_inactive;
     auto_off.armed = FALSE;
 
     floppy_flux_end();
 
     /* Turn off all output pins. */
-    for (i = 9; i <= 15; i++)
-        gpio_write_pin(gpio_floppy, i, O_FALSE);
-    memset(&pins, 0, sizeof(pins));
+    write_pin(densel, FALSE);
+    write_pin(sel0,   FALSE);
+    write_pin(mot0,   FALSE);
+    write_pin(dir,    FALSE);
+    write_pin(step,   FALSE);
+    write_pin(wgate,  FALSE);
+    write_pin(side,   FALSE);
 }
 
 void floppy_init(void)
 {
-    unsigned int i, GPI_bus;
+    floppy_mcu_init();
 
     /* Output pins, unbuffered. */
-    for (i = 9; i <= 15; i++)
-        gpio_configure_pin(gpio_floppy, i, GPO_bus);
-
-    gpio_configure_pin(gpio_floppy, pin_index, GPI_pull_down);
-    delay_us(10);
-    GPI_bus = (get_index() == LOW) ? GPI_pull_up : GPI_floating;
-    printk("Floppy Inputs: %sternal Pullup\n",
-           (GPI_bus == GPI_pull_up) ? "In" : "Ex");
+    configure_pin(densel, GPO_bus);
+    configure_pin(sel0,   GPO_bus);
+    configure_pin(mot0,   GPO_bus);
+    configure_pin(dir,    GPO_bus);
+    configure_pin(step,   GPO_bus);
+    configure_pin(wgate,  GPO_bus);
+    configure_pin(side,   GPO_bus);
+    configure_pin(wdata,  GPO_bus);
 
     /* Input pins. */
-    for (i = 6; i <= 8; i++)
-        gpio_configure_pin(gpio_floppy, i, GPI_bus);
-
-    /* RDATA/WDATA */
-    gpio_configure_pin(gpio_data, pin_rdata, GPI_bus);
-    gpio_configure_pin(gpio_data, pin_wdata, GPO_bus);
-    afio->mapr |= (AFIO_MAPR_TIM2_REMAP_PARTIAL_1
-                   | AFIO_MAPR_TIM3_REMAP_PARTIAL);
-
-    /* PB[15:0] -> EXT[15:0] */
-    afio->exticr1 = afio->exticr2 = afio->exticr3 = afio->exticr4 = 0x1111;
+    configure_pin(index,  GPI_bus);
+    configure_pin(trk0,   GPI_bus);
+    configure_pin(wrprot, GPI_bus);
 
     /* Configure INDEX-changed IRQs and timer. */
     timer_init(&index.delay_timer, index_delay_timer, NULL);
@@ -267,47 +238,11 @@ void floppy_init(void)
     exti->imr = exti->ftsr = m(pin_index);
     IRQx_set_prio(irq_index, INDEX_IRQ_PRI);
     IRQx_enable(irq_index);
-
-    /* RDATA Timer setup: 
-     * The counter runs from 0x0000-0xFFFF inclusive at full SYSCLK rate.
-     *  
-     * Ch.2 (RDATA) is in Input Capture mode, sampling on every clock and with
-     * no input prescaling or filtering. Samples are captured on the falling 
-     * edge of the input (CCxP=1). DMA is used to copy the sample into a ring
-     * buffer for batch processing in the DMA-completion ISR. */
-    tim_rdata->psc = 0;
-    tim_rdata->arr = 0xffff;
-    tim_rdata->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
-    tim_rdata->dier = TIM_DIER_CC2DE;
-    tim_rdata->cr2 = 0;
-
-    /* RDATA DMA setup: From the RDATA Timer's CCRx into a circular buffer. */
-    dma_rdata.cpar = (uint32_t)(unsigned long)&tim_rdata->ccr2;
-    dma_rdata.cmar = (uint32_t)(unsigned long)dma.buf;
-
-    /* WDATA Timer setup:
-     * The counter is incremented at full SYSCLK rate. 
-     *  
-     * Ch.1 (WDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then 
-     * O_FALSE until the counter reloads. By changing the ARR via DMA we alter
-     * the time between (fixed-width) O_TRUE pulses, mimicking floppy drive 
-     * timings. */
-    tim_wdata->psc = 0;
-    tim_wdata->ccmr1 = (TIM_CCMR1_CC1S(TIM_CCS_OUTPUT) |
-                        TIM_CCMR1_OC1M(TIM_OCM_PWM1));
-    tim_wdata->ccer = TIM_CCER_CC1E | ((O_TRUE==0) ? TIM_CCER_CC1P : 0);
-    tim_wdata->ccr1 = sysclk_ns(400);
-    tim_wdata->dier = TIM_DIER_UDE;
-    tim_wdata->cr2 = 0;
-
-    /* WDATA DMA setup: From a circular buffer into the WDATA Timer's ARR. */
-    dma_wdata.cpar = (uint32_t)(unsigned long)&tim_wdata->arr;
-    dma_wdata.cmar = (uint32_t)(unsigned long)dma.buf;
 }
 
 static struct gw_info gw_info = {
     .max_index = 15, .max_cmd = CMD_SELECT,
-    .sample_freq = SYSCLK_MHZ * 1000000u
+    .sample_freq = 72000000u
 };
 
 static void auto_off_arm(void)
@@ -349,7 +284,8 @@ static struct {
 static void rdata_encode_flux(void)
 {
     const uint16_t buf_mask = ARRAY_SIZE(dma.buf) - 1;
-    uint16_t cons = dma.cons, prod, prev = dma.prev_sample, curr, next;
+    uint16_t cons = dma.cons, prod;
+    timcnt_t prev = dma.prev_sample, curr, next;
     uint32_t ticks = rw.ticks_since_flux;
     int ticks_since_index = rw.ticks_since_index;
 
@@ -359,12 +295,12 @@ static void rdata_encode_flux(void)
     IRQ_global_disable();
 
     /* Find out where the DMA engine's producer index has got to. */
-    prod = ARRAY_SIZE(dma.buf) - dma_rdata.cndtr;
+    prod = ARRAY_SIZE(dma.buf) - dma_rdata.ndtr;
 
     if (rw.idx != index.count) {
         /* We have just passed the index mark: Record information about 
          * the just-completed revolution. */
-        int partial_flux = ticks + (uint16_t)(index.rdata_cnt - prev);
+        int partial_flux = ticks + (timcnt_t)(index.rdata_cnt - prev);
         rw.index_ticks[rw.idx++] = ticks_since_index + partial_flux;
         ticks_since_index = -partial_flux;
     }
@@ -409,10 +345,12 @@ static void rdata_encode_flux(void)
      * accumulator. This avoids 16-bit overflow and because, we take care to
      * keep the 16-bit timestamp at least 200us behind, we cannot race the next
      * flux timestamp. */
-    curr = tim_rdata->cnt - prev;
-    if (unlikely(curr > sysclk_us(400))) {
-        prev += sysclk_us(200);
-        ticks += sysclk_us(200);
+    if (sizeof(timcnt_t) == sizeof(uint16_t)) {
+        curr = tim_rdata->cnt - prev;
+        if (unlikely(curr > sysclk_us(400))) {
+            prev += sysclk_us(200);
+            ticks += sysclk_us(200);
+        }
     }
 
     /* Save our progress for next time. */
@@ -427,22 +365,18 @@ static uint8_t floppy_read_prep(struct gw_read_flux *rf)
     if ((rf->nr_idx == 0) || (rf->nr_idx > gw_info.max_index))
         return ACK_BAD_COMMAND;
 
-    /* Start DMA. */
-    dma_rdata.cndtr = ARRAY_SIZE(dma.buf);
-    dma_rdata.ccr = (DMA_CCR_PL_HIGH |
-                     DMA_CCR_MSIZE_16BIT |
-                     DMA_CCR_PSIZE_16BIT |
-                     DMA_CCR_MINC |
-                     DMA_CCR_CIRC |
-                     DMA_CCR_DIR_P2M |
-                     DMA_CCR_EN);
+    /* Prepare Timer & DMA. */
+    dma_rdata.mar = (uint32_t)(unsigned long)dma.buf;
+    dma_rdata.ndtr = ARRAY_SIZE(dma.buf);    
+    rdata_prep();
+    tim_rdata->egr = TIM_EGR_UG; /* update CNT, PSC, ARR */
+    tim_rdata->sr = 0; /* dummy write */
 
     /* DMA soft state. */
     dma.cons = 0;
     dma.prev_sample = tim_rdata->cnt;
 
-    /* Start timer. */
-    tim_rdata->ccer = TIM_CCER_CC2E | TIM_CCER_CC2P;
+    /* Start Timer. */
     tim_rdata->cr1 = TIM_CR1_CEN;
 
     index.count = 0;
@@ -533,7 +467,7 @@ static void floppy_read(void)
  * WRITE PATH
  */
 
-static unsigned int _wdata_decode_flux(uint16_t *tbuf, unsigned int nr)
+static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
 {
     unsigned int todo = nr;
     uint32_t x, ticks = rw.ticks_since_flux;
@@ -606,7 +540,7 @@ static void wdata_decode_flux(void)
     uint16_t nr_to_wrap, nr_to_cons, nr, dmacons;
 
     /* Find out where the DMA engine's consumer index has got to. */
-    dmacons = ARRAY_SIZE(dma.buf) - dma_wdata.cndtr;
+    dmacons = ARRAY_SIZE(dma.buf) - dma_wdata.ndtr;
 
     /* Find largest contiguous stretch of ring buffer we can fill. */
     nr_to_wrap = ARRAY_SIZE(dma.buf) - dma.prod;
@@ -652,8 +586,14 @@ static uint8_t floppy_write_prep(struct gw_write_flux *wf)
     if (get_wrprot() == LOW)
         return ACK_WRPROT;
 
+    wdata_prep();
+
+    /* WDATA DMA setup: From a circular buffer into the WDATA Timer's ARR. */
+    dma_wdata.par = (uint32_t)(unsigned long)&tim_wdata->arr;
+    dma_wdata.mar = (uint32_t)(unsigned long)dma.buf;
+
     /* Initialise DMA ring indexes (consumer index is implicit). */
-    dma_wdata.cndtr = ARRAY_SIZE(dma.buf);
+    dma_wdata.ndtr = ARRAY_SIZE(dma.buf);
     dma.prod = 0;
 
     floppy_state = ST_write_flux_wait_data;
@@ -689,13 +629,7 @@ static void floppy_write_wait_data(void)
     rw.start = time_now();
 
     /* Enable DMA only after flux values are generated. */
-    dma_wdata.ccr = (DMA_CCR_PL_HIGH |
-                     DMA_CCR_MSIZE_16BIT |
-                     DMA_CCR_PSIZE_16BIT |
-                     DMA_CCR_MINC |
-                     DMA_CCR_CIRC |
-                     DMA_CCR_DIR_M2P |
-                     DMA_CCR_EN);
+    dma_wdata_start();
 
     /* Preload timer with first flux value. */
     tim_wdata->egr = TIM_EGR_UG;
@@ -721,7 +655,7 @@ static void floppy_write_wait_index(void)
     tim_wdata->cr1 = TIM_CR1_CEN;
 
     /* Enable output. */
-    gpio_configure_pin(gpio_data, pin_wdata, AFO_bus);
+    configure_pin(wdata, AFO_bus);
     write_pin(wgate, TRUE);
 
     index.count = 0;
@@ -771,7 +705,7 @@ static void floppy_write(void)
             goto terminate;
         /* Check progress of draining the DMA ring. */
         prev_todo = todo;
-        dmacons = ARRAY_SIZE(dma.buf) - dma_wdata.cndtr;
+        dmacons = ARRAY_SIZE(dma.buf) - dma_wdata.ndtr;
         todo = (dma.prod - dmacons) & (ARRAY_SIZE(dma.buf) - 1);
     } while ((todo != 0) && (todo <= prev_todo));
 

+ 148 - 0
src/floppy_f1.c

@@ -0,0 +1,148 @@
+/*
+ * floppy_f1.c
+ * 
+ * Floppy interface control: STM32F103C8.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#define O_FALSE 1
+#define O_TRUE  0
+
+#define GPO_bus GPO_opendrain(_2MHz,O_FALSE)
+#define AFO_bus AFO_opendrain(_2MHz)
+static unsigned int GPI_bus;
+
+/* Input pins */
+#define gpio_index  gpiob
+#define pin_index   6 /* PB6 */
+#define gpio_trk0   gpiob
+#define pin_trk0    7 /* PB7 */
+#define gpio_wrprot gpiob
+#define pin_wrprot  8 /* PB8 */
+
+/* Output pins. */
+#define gpio_densel gpiob
+#define pin_densel  9 /* PB9 */
+#define gpio_sel0  gpiob
+#define pin_sel0   10 /* PB10 */
+#define gpio_mot0  gpiob
+#define pin_mot0   11 /* PB11 */
+#define gpio_dir   gpiob
+#define pin_dir    12 /* PB12 */
+#define gpio_step  gpiob
+#define pin_step   13 /* PB13 */
+#define gpio_wgate gpiob
+#define pin_wgate  14 /* PB14 */
+#define gpio_side  gpiob
+#define pin_side   15 /* PB15 */
+
+/* RDATA: Pin B3, Timer 2 Channel 2, DMA1 Channel 7. */
+#define gpio_rdata  gpiob
+#define pin_rdata   3
+#define tim_rdata   (tim2)
+#define dma_rdata   (dma1->ch7)
+
+/* WDATA: Pin B4, Timer 3 Channel 1, DMA1 Channel 3. */
+#define gpio_wdata  gpiob
+#define pin_wdata   4
+#define tim_wdata   (tim3)
+#define dma_wdata   (dma1->ch3)
+
+typedef uint16_t timcnt_t;
+#define TIM_PSC 0
+
+#define irq_index 23
+void IRQ_23(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI9_5 */
+
+#define irq_index_delay 31
+void IRQ_31(void) __attribute__((alias("IRQ_INDEX_delay")));
+
+static void floppy_mcu_init(void)
+{
+    /* Determine whether input pins must be internally pulled down. */
+    configure_pin(index, GPI_pull_down);
+    delay_us(10);
+    GPI_bus = (get_index() == LOW) ? GPI_pull_up : GPI_floating;
+    printk("Floppy Inputs: %sternal Pullup\n",
+           (GPI_bus == GPI_pull_up) ? "In" : "Ex");
+
+    /* Remap timers to RDATA/WDATA pins. */
+    afio->mapr |= (AFIO_MAPR_TIM2_REMAP_PARTIAL_1
+                   | AFIO_MAPR_TIM3_REMAP_PARTIAL);
+
+    /* Set up EXTI mapping for INDEX: PB[15:0] -> EXT[15:0] */
+    afio->exticr1 = afio->exticr2 = afio->exticr3 = afio->exticr4 = 0x1111;
+
+    configure_pin(rdata, GPI_bus);
+}
+
+static void rdata_prep(void)
+{
+    /* RDATA Timer setup: 
+     * The counter runs from 0x0000-0xFFFF inclusive at full SYSCLK rate.
+     *  
+     * Ch.2 (RDATA) is in Input Capture mode, sampling on every clock and with
+     * no input prescaling or filtering. Samples are captured on the falling 
+     * edge of the input (CCxP=1). DMA is used to copy the sample into a ring
+     * buffer for batch processing in the DMA-completion ISR. */
+    tim_rdata->psc = 0;
+    tim_rdata->arr = 0xffff;
+    tim_rdata->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
+    tim_rdata->dier = TIM_DIER_CC2DE;
+    tim_rdata->cr2 = 0;
+
+    /* RDATA DMA setup: From the RDATA Timer's CCRx into a circular buffer. */
+    dma_rdata.par = (uint32_t)(unsigned long)&tim_rdata->ccr2;
+    dma_rdata.cr = (DMA_CR_PL_HIGH |
+                    DMA_CR_MSIZE_16BIT |
+                    DMA_CR_PSIZE_16BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_CIRC |
+                    DMA_CR_DIR_P2M |
+                    DMA_CR_EN);
+
+    tim_rdata->ccer = TIM_CCER_CC2E | TIM_CCER_CC2P;
+}
+
+static void wdata_prep(void)
+{
+    /* WDATA Timer setup:
+     * The counter is incremented at full SYSCLK rate. 
+     *  
+     * Ch.1 (WDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then 
+     * O_FALSE until the counter reloads. By changing the ARR via DMA we alter
+     * the time between (fixed-width) O_TRUE pulses, mimicking floppy drive 
+     * timings. */
+    tim_wdata->psc = 0;
+    tim_wdata->ccmr1 = (TIM_CCMR1_CC1S(TIM_CCS_OUTPUT) |
+                        TIM_CCMR1_OC1M(TIM_OCM_PWM1));
+    tim_wdata->ccer = TIM_CCER_CC1E | ((O_TRUE==0) ? TIM_CCER_CC1P : 0);
+    tim_wdata->ccr1 = sysclk_ns(400);
+    tim_wdata->dier = TIM_DIER_UDE;
+    tim_wdata->cr2 = 0;
+}
+
+static void dma_wdata_start(void)
+{
+    dma_wdata.cr = (DMA_CR_PL_HIGH |
+                    DMA_CR_MSIZE_16BIT |
+                    DMA_CR_PSIZE_16BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_CIRC |
+                    DMA_CR_DIR_M2P |
+                    DMA_CR_EN);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 151 - 0
src/floppy_f7.c

@@ -0,0 +1,151 @@
+/*
+ * floppy_f7.c
+ * 
+ * Floppy interface control: STM32F730x8.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#define O_FALSE 1
+#define O_TRUE  0
+
+#define GPO_bus GPO_opendrain(IOSPD_LOW,O_FALSE)
+#define AFO_bus AFO_opendrain(IOSPD_LOW)
+#define GPI_bus GPI_floating
+
+/* Input pins */
+#define gpio_index  gpiob
+#define pin_index   2 /* PB2 */
+#define gpio_trk0   gpioa
+#define pin_trk0    3 /* PA3 */
+#define gpio_wrprot gpioa
+#define pin_wrprot  1 /* PA1 */
+
+/* Output pins. */
+#define gpio_densel gpiob
+#define pin_densel 12 /* PB12 */
+#define gpio_sel0  gpiob
+#define pin_sel0   11 /* PB11 */
+#define gpio_mot0  gpiob
+#define pin_mot0   1  /* PB1 */
+#define gpio_sel1  gpiob
+#define pin_sel1   0  /* PB0 */
+#define gpio_mot1  gpiob
+#define pin_mot1   10 /* PB10 */
+#define gpio_dir   gpioc
+#define pin_dir    4  /* PC4 */
+#define gpio_step  gpioa
+#define pin_step   7  /* PA7 */
+#define gpio_wgate gpioa
+#define pin_wgate  6  /* PA6 */
+#define gpio_side  gpioc
+#define pin_side   3  /* PC3 */
+
+/* RDATA: Pin A0, Timer 2 Channel 1, DMA1 Stream 5 Channel 3. */
+#define gpio_rdata  gpioa
+#define pin_rdata   0
+#define tim_rdata   (tim2)
+#define dma_rdata   (dma1->str[5])
+
+/* WDATA: Pin A2, Timer 2 Channel 3, DMA1 Stream 1 Channel 3. */
+#define gpio_wdata  gpioa
+#define pin_wdata   2
+#define tim_wdata   (tim2)
+#define dma_wdata   (dma1->str[1])
+
+typedef uint32_t timcnt_t;
+#define TIM_PSC (SYSCLK_MHZ/72)
+
+#define irq_index 8
+void IRQ_8(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI2 */
+
+#define irq_index_delay 31
+void IRQ_31(void) __attribute__((alias("IRQ_INDEX_delay")));
+
+static void floppy_mcu_init(void)
+{
+    /* Enable clock for Timer 2. */
+    rcc->apb1enr |= RCC_APB1ENR_TIM2EN;
+    peripheral_clock_delay();
+
+    /* Set RDATA/WDATA as Timer pins. */
+    gpio_set_af(gpio_rdata, pin_rdata, 1);
+    gpio_set_af(gpio_wdata, pin_wdata, 1);
+    configure_pin(rdata, AFI(PUPD_none));
+
+    /* Set up EXTI mapping for INDEX: PB[3:0] -> EXT[3:0] */
+    syscfg->exticr1 = 0x1111;
+}
+
+static void rdata_prep(void)
+{
+    /* RDATA Timer setup: 
+     * The counter runs from 0x00000000-0xFFFFFFFF inclusive.
+     * The prescaler is used to reduce the clock rate to 72MHz.
+     *  
+     * Ch.1 (RDATA) is in Input Capture mode, sampling on every clock and with
+     * no input prescaling or filtering. Samples are captured on the falling 
+     * edge of the input (CCxP=1). DMA is used to copy the sample into a ring
+     * buffer for batch processing in the DMA-completion ISR. */
+    tim_rdata->psc = TIM_PSC-1;
+    tim_rdata->arr = 0xffffffff;
+    tim_rdata->ccmr1 = TIM_CCMR1_CC1S(TIM_CCS_INPUT_TI1);
+    tim_rdata->dier = TIM_DIER_CC1DE;
+    tim_rdata->cr2 = 0;
+
+    /* RDATA DMA setup: From the RDATA Timer's CCRx into a circular buffer. */
+    dma_rdata.par = (uint32_t)(unsigned long)&tim_rdata->ccr1;
+    dma_rdata.cr = (DMA_CR_CHSEL(3) |
+                    DMA_CR_PL_HIGH |
+                    DMA_CR_MSIZE_32BIT |
+                    DMA_CR_PSIZE_32BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_CIRC |
+                    DMA_CR_DIR_P2M);
+    dma_rdata.cr |= DMA_CR_EN;
+
+    tim_rdata->ccer = TIM_CCER_CC1E | TIM_CCER_CC1P;
+}
+
+static void wdata_prep(void)
+{
+    /* WDATA Timer setup:
+     * The counter is incremented at 72MHz.
+     *  
+     * Ch.3 (WDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then 
+     * O_FALSE until the counter reloads. By changing the ARR via DMA we alter
+     * the time between (fixed-width) O_TRUE pulses, mimicking floppy drive 
+     * timings. */
+    tim_wdata->psc = TIM_PSC-1;
+    tim_wdata->ccmr2 = (TIM_CCMR2_CC3S(TIM_CCS_OUTPUT) |
+                        TIM_CCMR2_OC3M(TIM_OCM_PWM1));
+    tim_wdata->ccer = TIM_CCER_CC3E | ((O_TRUE==0) ? TIM_CCER_CC3P : 0);
+    tim_wdata->ccr3 = sysclk_ns(400) / TIM_PSC;
+    tim_wdata->dier = TIM_DIER_UDE;
+    tim_wdata->cr2 = 0;
+}
+
+static void dma_wdata_start(void)
+{
+    dma_wdata.cr = (DMA_CR_CHSEL(3) |
+                    DMA_CR_PL_HIGH |
+                    DMA_CR_MSIZE_32BIT |
+                    DMA_CR_PSIZE_32BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_CIRC |
+                    DMA_CR_DIR_M2P);
+    dma_wdata.cr |= DMA_CR_EN;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 0 - 3
src/main.c

@@ -43,9 +43,6 @@ int main(void)
     floppy_init();
     usb_init();
 
-    /* XXX */
-    gpio_configure_pin(gpioa, 15, GPO_pushpull(_2MHz, HIGH));
-
     for (;;) {
         canary_check();
         usb_process();

+ 0 - 10
src/stm32f7.c

@@ -10,19 +10,9 @@
  */
 
 /* XXX */
-void floppy_init(void) {}
-void floppy_process(void) {}
 void fpec_init(void) {}
 void fpec_page_erase(uint32_t flash_address) {}
 void fpec_write(const void *data, unsigned int size, uint32_t flash_address) {}
-#ifndef BOOTLOADER
-static void floppy_reset(void) {}
-static void floppy_configure(void) {}
-const struct usb_class_ops usb_cdc_acm_ops = {
-    .reset = floppy_reset,
-    .configure = floppy_configure
-};
-#endif
 
 static void clock_init(void)
 {