MDT10F684 (PIC16F684 Clone) Problems & Fixes — Complete Guide

MDT10F684 (PIC16F684 Clone): Problems, Fixes & Beginner's Guide
MDT10F684 (PIC16F684 Clone): Problems, Fixes & Beginner's Guide
July 8, 2026
MDT10F684 (PIC16F684 Clone): Problems, Fixes & Beginner's Guide

 

The MDT10F684 is a popular low-cost clone of Microchip's PIC16F684, widely available in Pakistan's electronics markets at a fraction of the original's price. The datasheet says it is fully code-compatible — and that is true. What the datasheet does not tell you is that the configuration is completely different, and that popular C compilers can silently sabotage one of its best features.

At Digilog, we recently ported a working PIC16F684 solar boost-converter design (360V output, 20 kHz PWM) to the MDT10F684. The same HEX file that ran perfectly on the genuine PIC failed on the clone. It took a full day of systematic debugging — datasheet comparison, register-level tests, and a serial diagnostic — to find every difference.

This guide documents every trap we hit, so you can avoid them in minutes instead of days.

Why Your PIC16F684 Code Fails on the MDT10F684

First, the good news. The two chips are genuinely compatible where it matters:

Feature PIC16F684 MDT10F684
Program Flash 2048 × 14-bit words 2048 × 14-bit words
SRAM / EEPROM 128 B / 256 B 128 B / 256 B
Instruction set 35 instructions Compatible
ADC, timers, ECCP PWM 10-bit, 8-ch Same peripherals

So if your program worked on the PIC, the logic will work on the MDT. The failures come from three places: fuse configuration, the oscillator setting, and the ADC reference. Let's take them one by one.

Trap 1: The Config Word at 0x2007 Is Ignored

On a genuine PIC16F684, your compiler's #fuses line (CCS) or __CONFIG directive is stored at address 0x2007 inside the HEX file, and the programmer writes it to the chip automatically.

The MDT10F684 does not use address 0x2007 at all. Its configuration lives in two separate registers at 0x1200 and 0x1201, with a completely different bit layout. When you load your HEX into the MDT programmer (typically YS-Writer Pro), the 0x2007 word is simply discarded.

What this means in practice:

  • Your #fuses INTRC_IO, NOWDT, PUT, NOMCLR, NOBROWNOUT line does nothing on the MDT.
  • Every fuse must be set manually in the YS-Writer "Edit Option" dialog.
  • The dialog is the only source of truth. If the chip misbehaves, check the dialog first — not your code.

The upside: the 0x2007 word cannot corrupt anything either. The MDT's flash is only 0x000–0x7FF, so the stray config word lands nowhere.

Trap 2: "HF" Does NOT Mean Internal Oscillator

This one cost us the most time, and it will catch anyone coming from Microchip's naming.

On genuine PICs, the internal oscillator block is called HFINTOSC — so when you see "HF" in YS-Writer's OSC Type dropdown, it feels like the internal oscillator. It is not. In MDT naming:

YS-Writer option Actual meaning
HF High-speed external crystal on PA4/PA5
XT / LF External crystal (standard / low-power)
EC External clock input
RC / RC+IO External resistor-capacitor oscillator
IRC+IO Internal RC oscillator, both pins free as I/O
IRC Internal RC, CLKOUT on PA4

If your code uses INTRC_IO (CCS) or FOSC_INTOSCIO, the correct YS-Writer setting is IRC+IO. Select HF by mistake and the chip waits for a crystal that doesn't exist — nothing runs, or it limps along erratically.

Once set correctly, the MDT's internal oscillator is surprisingly good: we measured it within 0.5% of nominal using the serial test below — accurate enough for bit-banged UART at 9600 baud.

Trap 3: Default Fuses That Reset Your Chip Randomly

Three more dialog options ship with dangerous defaults. Match them to your PIC code:

YS-Writer option Common default Should be (typical) Symptom if wrong
WDT Enable Disable (if code has NOWDT) Chip resets every ~2 s — code appears to "restart randomly"
LVR (brownout) Always On @ 3.8V Disable (if code has NOBROWNOUT) Random resets whenever 5V rail dips — fatal on switching-converter boards
MCLRE MCLR enabled PA3 (if code has NOMCLR) Floating MCLR pin picks up noise → resets, or chip held in reset
PWRTE Disable Enable (if code has PUT) Unstable start on slow-rising supplies

A watchdog that your code never clears, plus a brownout detector on a noisy board, produces the classic clone complaint: "it starts, blinks once, then goes mad." It isn't the silicon — it's the fuses.

Trap 4: The ADC 2V Reference — and the Compiler That Sabotages It

The MDT10F684 has a feature the original PIC lacks: a selectable internal ADC reference of 2V, 3V or 4V, independent of the supply rail. Local engineers prefer it — several told us the ADC is more stable on the internal 2V reference than on the noisy 5V VDD.

But there is a hidden conflict. The reference is selected by the ADVRS bits (bits 6–5 of register ADS0, address 0x1F):

  • 00 = VDD reference
  • 10 = external reference on PA1
  • x1 = internal reference (2/3/4V chosen by the "ADC Vref" fuse)

Here's the trap: on a genuine PIC16F684, bit 5 of ADCON0 is unimplemented. CCS C's library functions (setup_adc_ports(), set_adc_channel(), read_adc()) were written for the genuine PIC, so they write bit 5 as 0 — every single time. Result: you set "ADC Vref: 2V" in the programmer, but your compiled code silently switches the reference back to VDD at runtime. Your ADC scale is wrong and you'll never see why in your C source.

We proved this on real silicon with a register read-back test: the CCS library cleared ADVRS0 on every ADC call.

The fix: bypass the library. Drive the registers directly. This driver is verified working:

// ---- MDT10F684 ADC registers ----
#byte ADS0    = 0x1F      // ADFM | ADVRS1 ADVRS0 | CHS2 CHS1 CHS0 | GO | ADON
#byte ADS1    = 0x9F      // ADRVO | ADCS2 ADCS1 ADCS0 | ----
#byte ADRESH  = 0x1E
#byte ADRESL  = 0x9E
#byte ANSEL_R = 0x91
#byte TRISA_R = 0x85
#bit  GO_BIT  = ADS0.1

// ADFM=1, ADVRS=01 (internal ref per fuse), CHS=010 (AN2), ADON=1
#define ADS0_VAL   0b10101001    // 0xA9

void adc_init(void) {
    TRISA_R |= 0b00000100;   // RA2 = input
    ANSEL_R  = 0b00000100;   // AN2 analog, rest digital
    ADS1     = 0b01010000;   // ADCS=101 -> FOSC/16 (2us TAD @ 8MHz)
    ADS0     = ADS0_VAL;
}

int16 adc_read(void) {
    ADS0 = ADS0_VAL;         // re-assert config before EVERY read
    delay_us(20);            // let the sample/hold capacitor settle
    GO_BIT = 1;
    while (GO_BIT);          // hardware clears GO when done
    return ((int16)ADRESH << 8) | ADRESL;
}

Two details matter:

  1. Re-write ADS0 before every conversion. Even if some library call touches the register, the config is guaranteed correct 20 µs before each read.
  2. Keep the "ADC Vref" fuse and the code in agreement. The ADVRS bits say use the internal reference; the fuse says which voltage it is. If someone later reprograms the chip with the fuse at 4V, all your ADC thresholds silently shift.

And remember to rescale your constants. A threshold computed for a 5V reference reads 2.5× higher on a 2V reference. Recalculate every ADC compare value: ADC = (Vin / Vref) × 1023.

How to Verify Everything: The Serial Test Method

Before trusting the chip in a real product, run this 10-minute test. It verifies the oscillator and the ADC simultaneously, using only a potentiometer and a USB-TTL adapter (the MDT10F684 has no hardware UART, so we bit-bang serial — which is itself an oscillator test: if the clock is off, the output is garbage).

Setup: pot across VDD–GND, wiper → AN2 (pin 11); TX = RC0 (pin 10) → USB-TTL RX; terminal at 9600 8N1.

void main() {
   int16 raw, vpin_mv;
   int16 seconds = 0;
   adc_init();
   delay_ms(50);
   printf("MDT10F684 TEST - ADS0=%X (want A9)\r\n", ADS0);
   while(TRUE) {
      raw     = adc_read();
      vpin_mv = (int16)(((int32)raw * 2000L) / 1023);  // 2V ref
      seconds++;
      printf("T=%lu s  ADC=%lu  Vpin=%lu mV\r\n", seconds, raw, vpin_mv);
      delay_ms(955);   // ~1s per line incl. transmit time
   }
}

Reading the results:

  • Clean, readable text → oscillator is close to 8 MHz. Garbage → wrong OSC fuse.
  • T= counter tracks a stopwatch (compare at T=60) → oscillator accuracy confirmed. Actual frequency = 8 MHz × T ÷ stopwatch seconds.
  • ADC sweeps 0→1023 smoothly as you turn the pot → converter linear.
  • Where it rails at 1023 reveals the true reference: at ~2.0V = internal 2V active ✅; at ~5V = your code is still forcing VDD (the CCS trap above).
  • ADS0=A9 in the printout proves the reference bits latched.

On our chip this test showed the oscillator within 0.5% and a perfectly linear ADC — after which the full solar converter ran exactly as it did on the genuine PIC.

Quick-Reference: Correct YS-Writer Settings for CCS-Style Code

For code written with #fuses INTRC_IO, NOWDT, PUT, NOMCLR, NOBROWNOUT:

Option Setting
OSC Type IRC+IO
IESO / FCMEN Disable
MCLRE PA3 (MCLR off)
RD PORT RD Pins
PWRTE Enable
WDT Disable
LVR Disable
Security / EE Security Disable (while developing)
ADC Vref 2V (if using internal reference — must match code)

Frequently Asked Questions

Is the MDT10F684 a drop-in replacement for the PIC16F684?

The silicon is code-compatible — same memory, instructions and peripherals. But it is not drop-in for your workflow: fuses must be set in the MDT programmer dialog (not the HEX), and compiler ADC libraries need the manual driver above if you use the internal reference.

Why does my PICkit not detect the MDT10F684?

The clone reports a different device ID and uses different config addresses, so Microchip tools reject it. Use the MDT-compatible programmer (YS-Writer Pro with the AP07 adapter) instead.

My MDT10F684 keeps resetting every couple of seconds. Why?

Almost always the WDT fuse defaulted to Enable in YS-Writer while your code never clears the watchdog. Set WDT = Disable. If resets correlate with load switching, also disable LVR (brownout) and turn MCLRE off.

Which ADC reference should I use — VDD or internal 2V?

If your 5V rail is clean and well-decoupled, VDD reference works and needs no special code. On noisy boards (SMPS, motor drivers), the internal 2V reference is independent of rail sag — but requires the manual register driver above, because compiler libraries overwrite the selection bits.

Can I use 115200-baud serial on the MDT10F684?

Not reliably. There is no hardware UART, and at 8 MHz a bit-banged UART only has ~17 instruction cycles per bit at 115200. Stick to 9600–38400 baud on the internal RC oscillator.

Conclusion

The MDT10F684 is a genuinely capable PIC16F684 clone — our converter now runs on it with an oscillator accurate to half a percent and a rock-stable ADC. Every problem we hit traced back to configuration differences, not bad silicon: fuses live in the programmer dialog, "HF" means external crystal, the watchdog defaults on, and C-compiler ADC libraries fight the reference selection.

Set the fuses from the table above, use the manual ADC driver if you want the internal reference, and validate with the serial test before deploying — and this chip will save you real money on production runs.

Need the parts? Digilog stocks microcontrollers, programmers, USB-TTL serial adapters and everything else used in this guide. Browse our Microcontrollers collection and STM32 & Microchip (PIC) MCUs .

Here is complete test code

///////////////////////////////////////////////////////////////////////////////
// MDT10F684 - ADC DIAGNOSTIC TEST - INTERNAL 2V REFERENCE
// Fully manual ADC driver (no CCS ADC library - nothing can clobber ADVRS)
//
// Hardware:
//   - Pot across VDD-GND, wiper -> AN2 (RA2, pin 11)
//   - Serial TX = RC0 (pin 10) -> RX of USB-TTL adapter, 9600 8N1, common GND
//
// YS-Writer fuses:
//   OSC=IRC+IO, IESO=Dis, FCMEN=Dis, MCLRE=PA3(off), RD PORT=RD Pins,
//   PWRTE=En, WDT=Dis, LVR=Dis, Security=Dis, EE Sec=Dis, ADC Vref=2V
//
// Output line:  T=12 s  ADC=1023  Vpin=2000 mV  W=A9  A=A9
//   W = ADS0 immediately after write (did the bit latch?)
//   A = ADS0 after conversion        (did it survive?)
//
// Decode:
//   W=A9 A=A9  ADC rails 1023 @2.5V  -> 2V internal ref WORKING
//   W=A9 A=89                        -> HW clears bit during conversion
//   W=89 A=89                        -> ADVRS0 not implemented -> plan B (TL431)
//   W=A9 A=A9  but ADC=512 @2.5V     -> register lies, silicon ignores -> plan B
///////////////////////////////////////////////////////////////////////////////
#include <16F684.h>
#device adc=10

// NOTE: MDT writer IGNORES these #fuses - set everything in YS-Writer dialog.
#fuses INTRC_IO, NOWDT, PUT, NOMCLR, NOBROWNOUT
#use delay(internal=8MHz)
#use rs232(baud=9600, xmit=PIN_C0, parity=N, bits=8, stream=DBG)

// ---- MDT10F684 ADC registers (CCS handles bank switching) ------------------
#byte ADS0    = 0x1F      // ADFM | ADVRS1 ADVRS0 | CHS2 CHS1 CHS0 | GO | ADON
#byte ADS1    = 0x9F      // ADRVO | ADCS2 ADCS1 ADCS0 | ----
#byte ADRESH  = 0x1E      // result high
#byte ADRESL  = 0x9E      // result low
#byte ANSEL_R = 0x91      // ANINS: analog pin select
#byte TRISA_R = 0x85      // CPIOA: 1 = input

#bit  GO_BIT = ADS0.1

// ADS0: ADFM=1 (right-justified), ADVRS=01 (internal ref, 2V per fuse),
//       CHS=010 (AN2), GO=0, ADON=1
#define ADS0_VAL   0b10101001      // 0xA9

#define VREF_MV     2000L          // internal 2V reference, in millivolts
#define LOOP_MS     1000
#define TX_COMP_MS    45           // ADC + serial TX time compensation

// ---- diagnostics ------------------------------------------------------------
int8 reg_before;    // ADS0 right after write, before conversion
int8 reg_after;     // ADS0 right after conversion completes

// ---- manual ADC driver ------------------------------------------------------
void my_adc_init(void) {
   TRISA_R |= 0b00000100;    // RA2/PA2 = input
   ANSEL_R  = 0b00000100;    // AN2 analog, everything else digital
   ADS1     = 0b01010000;    // ADCS=101 -> FOSC/16 (2us TAD @8MHz), ADRVO=0
   ADS0     = ADS0_VAL;      // reference + channel + ADC on
}

int16 my_read_adc(void) {
   ADS0 = ADS0_VAL;          // re-assert full config before EVERY read
   reg_before = ADS0;        // SNAPSHOT 1: did the write latch?
   delay_us(20);             // acquisition: let S/H cap settle
   GO_BIT = 1;               // start conversion
   while (GO_BIT);           // hardware clears GO when done
   reg_after = ADS0;         // SNAPSHOT 2: did config survive conversion?
   return ((int16)ADRESH << 8) | ADRESL;
}

// ---- test program -----------------------------------------------------------
void main() {
   int16 raw, vpin_mv;
   int16 seconds = 0;

   my_adc_init();
   delay_ms(50);

   printf("\r\nMDT10F684 MANUAL-ADC TEST, INTERNAL 2V REF\r\n");
   printf("ADS0 after init = %X (want A9)\r\n", ADS0);

   while(TRUE) {
      raw     = my_read_adc();
      vpin_mv = (int16)(((int32)raw * VREF_MV) / 1023);

      seconds++;
      printf("T=%lu s  ADC=%lu  Vpin=%lu mV  W=%X  A=%X\r\n",
              seconds, raw, vpin_mv, reg_before, reg_after);

      delay_ms(LOOP_MS - TX_COMP_MS);
   }
}



RELATED ARTICLES