swddude -- A SWD programmer for ARM Cortex microcontrollers.

Introducing swddude

But, many Cortex-M parts are small — 4x4 millimeters small — and don’t have the pins left over for JTAG.

For these parts, ARM introduced a new debug interface, called SWD.

Unfortunately, SWD isn’t well-supported by open-source tools.

Support is in progress in most of them — including my personal favorite,

OpenOCD — but I’ve had bad luck so far.

Anton Staaf was having the same issue, and decided to do something about it.

He tricked the cheap, commonly-available FTDI FT232H chip into speaking the line-level SWD protocol.

We’ve teamed up and, a week or so later, have something to show for it.

Presenting: swddude, the dead-simple programmer for SWD microcontrollers.

(By “dead-simple” I mean “rather braindead” but it works!)

Currently it can flash code onto the LPC111x, LPC11Cxx, and LPC13xx series. Support for more chips is in progress.

I’ve posted a mirror of the code on GitHub for your cloning pleasure. Happy hacking!

 

swddude 

swddude is very young pre-alpha software.

Caveat downloader.

swddude is a collection of simple tools for programming a

nd using ARM Cortex microcontrollers, such as the Cortex-M0 and M3, using the SWD protocol.

Why?

Larger ARM microcontrollers have JTAG interfaces.

OpenOCD and friends already do a great job flashing these micros.

But for smaller parts, such as the LPC11xx/LPC13xx series, ARM has defined a new low-pin-count debug interface called SWD.

OpenOCD doesn't yet support SWD.

In fact, when we started writing swddude, no software on Mac or Linux could do what we wanted!

It's a shame not to be able to use these powerful little microcontrollers, so we wrote swddude to scratch this itch.

What Can It Do?

Currently:

  • swddude itself can flash code onto the NXP LPC11xx (Cortex-M0 based) and LPC13xx (Cortex-M3 based).
  • swdprobe can interrogate a SWD-compatible chip and dump information about what it finds.
    This is useful when adding support for new chips to swddude.
  • swdhost provides semihosting I/O for an attached microcontroller.
    With semihosting, embedded software can send printf-style messages
    to a host computer through the debug connection -- no UART required.
  • swddump extracts the contents of Flash from a supported microcontroller.

We're working to extend the tools to support more microcontroller varieties.
Specifically, we're focusing on microcontrollers without JTAG ports --
devices that can't be easily added to OpenOCD (yet).

How Do I Use It?

You'll need a supported programmer and, of course, a supported microcontroller with a SWD interface.
Currently swddude supports using the Bus Blaster (v2.5 programmed with the KT-link compatible CPLD configuration)
or any FTDI development board with an FT232H or FT2232H chip that has been wired to the SWD lines of your microcontroller.

Wire up your micro using the configuration described in swd_mpsse.h.

Install libusb 1.0 and the libusb-compat package.

The version supplied by your package manager (apt, Homebrew, etc.) should be fine.

Build a recent version of libftdi.

As of this writing, you must build libftdi from HEAD -- the released version (0.20)

still uses the legacy libusb 0.1 APIs and is incompatible with swddude.

After checking out swddude, build it like so:

$ cd swddude/source
$ make swddude release

This will deposit a swddude binary in swddude/source/release.

To program your microcontroller, you'll need to have your desired firmware in binary format

-- not ELF, and not Intel hex. Assuming it's in a file called firmware.bin, you run:

$ swddude -flash firmware.bin -fix_lpc_checksum

This will default to the um232h programmer (FTDI's FT232H development board) configuration.

If you are using a Bus Blaster, you should add -programmer bus_blaster to the command line above.

That last option, -fix_lpc_checksum, adds the vector table checksum expected by the NXP LPC series.

Without it, your firmware won't run! If some other tool has already

written the correct checksum into your firmware, you can omit that option.

Status and Known Issues

These boards are known to work:

  • LPCxpresso LPC1114.
  • LPCxpresso LPC11C24.
  • LPCxpresso LPC1343.

Note that the LPCxpresso boards will only work if you disable the proprietary LPC-Link programming device on the board.

On newer boards, you can do this by clearing solder jumpers between the two sections of the board;

older boards make you physically cut traces in the same position.

Known issues:

  • Error reporting is not great. Most failures just print a stack trace,
    which isn't helpful if you're not familiar with the source code.
    In general, it's worth retrying at least once -- sometimes the SWD communications just need to be reset.

  • swddude makes no attempt at identifying the chip it's programming.
    If you try programming an unsupported chip, it may do very bad things -- there is no safety net.

  • On the LPC1343 specifically, the SWD interfaces sometimes gets "stuck" and requires a power-cycle.
    This will show up as failures very early during communication (often referencing the IDCODE register).

Brief Tour of the Source

The source code contains the following top-level directories:

  • build: Anton Staaf's build system.
  • libs: Anton Staaf's support libraries, several of which we use.
    (We currently include more than we strictly need here.)
  • sourceswddude and friends.

The source tree uses git's submodule feature aggressively.

If you check out swddude using a simplegit clone,

you'll be left with empty directories that won't build. You can fix this by running

git submodule update --recursive

Alternatively, you can make this happen automatically when you clone, like so:

git clone --recursive ${git_url}


//
// Copyright (c) 2013 SILICON LABORATORIES, INC.
//
// FILE NAME    : dp_swd.c
// TARGET MCU   : C8051F380
// DESCRIPTION  : ARM CoreSight SW-DP Interface
//
// This file implements an interface to the ARM CoreSight Serial Wire Debug
// (SWD) - Debug Port (DP).
//
#include <compiler_defs.h>
#include "32bit_prog_defs.h"

//-----------------------------------------------------------------------------
// Project Variables
//-----------------------------------------------------------------------------

// Holds the last acknowledge error from the start of a move command.
// Also used by the Serial Wire module.
U8 idata ack_error;

#if __C51__
// Note how the bit addresses are arranged to provide an endian swap.
// io_word is stored BE (matches the Keil C compliler), while the bit addresses
// are LE (matches the wire interface).
SEGMENT_VARIABLE (io_word, UU32, SEG_BDATA);

// Used to provide bit addressable data for 8-bit and smaller shift routines.
// Also used by the Serial Wire module.
SEGMENT_VARIABLE (io_byte, U8, SEG_BDATA);

SBIT (iow_0, io_word.U32, 24);
SBIT (iow_1, io_word.U32, 25);
SBIT (iow_2, io_word.U32, 26);
SBIT (iow_3, io_word.U32, 27);
SBIT (iow_4, io_word.U32, 28);
SBIT (iow_5, io_word.U32, 29);
SBIT (iow_6, io_word.U32, 30);
SBIT (iow_7, io_word.U32, 31);

SBIT (iow_8, io_word.U32, 16);
SBIT (iow_9, io_word.U32, 17);
SBIT (iow_10, io_word.U32, 18);
SBIT (iow_11, io_word.U32, 19);
SBIT (iow_12, io_word.U32, 20);
SBIT (iow_13, io_word.U32, 21);
SBIT (iow_14, io_word.U32, 22);
SBIT (iow_15, io_word.U32, 23);

SBIT (iow_16, io_word.U32, 8);
SBIT (iow_17, io_word.U32, 9);
SBIT (iow_18, io_word.U32, 10);
SBIT (iow_19, io_word.U32, 11);
SBIT (iow_20, io_word.U32, 12);
SBIT (iow_21, io_word.U32, 13);
SBIT (iow_22, io_word.U32, 14);
SBIT (iow_23, io_word.U32, 15);

SBIT (iow_24, io_word.U32, 0);
SBIT (iow_25, io_word.U32, 1);
SBIT (iow_26, io_word.U32, 2);
SBIT (iow_27, io_word.U32, 3);
SBIT (iow_28, io_word.U32, 4);
SBIT (iow_29, io_word.U32, 5);
SBIT (iow_30, io_word.U32, 6);
SBIT (iow_31, io_word.U32, 7);

SBIT (iob_0, io_byte, 0);
SBIT (iob_1, io_byte, 1);
SBIT (iob_2, io_byte, 2);
SBIT (iob_3, io_byte, 3);
SBIT (iob_4, io_byte, 4);
SBIT (iob_5, io_byte, 5);
SBIT (iob_6, io_byte, 6);
SBIT (iob_7, io_byte, 7);
#else
UU32 io_word;
volatile U8 bdata io_byte;
#endif

//-----------------------------------------------------------------------------
// Variables Declarations
//-----------------------------------------------------------------------------

// Controls SW connection sequence. 0=SW-DP, 1=SWJ-DP (use switch sequence)
U8 idata swj_dp_type;

// Even parity lookup table, holds even parity result for a 4-bit value.
const U8 code even_parity[] =
{
    0x00, 0x10, 0x10, 0x00,
    0x10, 0x00, 0x00, 0x10,
    0x10, 0x00, 0x00, 0x10,
    0x00, 0x10, 0x10, 0x00
};


//-----------------------------------------------------------------------------
// SWD Host Command Handlers
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// StrobeSWCLK
//-----------------------------------------------------------------------------
//
// Pulls SWCLK high then low.
//
#define _StrobeSWCLK  { SWCLK_Out = 1; SWCLK_Out = 0; }


//-----------------------------------------------------------------------------
// SWD_Initialize
//-----------------------------------------------------------------------------
//
// Initializes SWD configuration data at startup.
//
void SWD_Initialize(void)
{
    swj_dp_type = FALSE;    // Default DP type is DP-SW
}

//-----------------------------------------------------------------------------
// (0x20) SWD_Configure
//-----------------------------------------------------------------------------
//
// Sets the debug port (DP) type to either Serial Wire only or Serial Wire JTAG.
// The firmware needs to know this because the connection sequence is different
// depending on the DP type.
//
// Parameters:
//    1. DP_Type - Debug Port type. 0=SW, 1=SWJ
//
// Returns:
//    1. HOST_COMMAND_OK
//
STATUS SWD_Configure(U8 dp_type)
{
    swj_dp_type = dp_type;

    return HOST_COMMAND_OK;
}

//-----------------------------------------------------------------------------
// (0x21) SWD_Connect
//-----------------------------------------------------------------------------
//
// Sets the target device for Serial Wire communication and returns the
// 32-bit ID code. Must be called before performing any SWD commands.
//
// Returns:
//  1-4. IDCODE - Value read from the IDCODE register (32-bit).
//    5. Response code.
//
STATUS SWD_Connect(void)
{
    U8 rtn;

    // Initialize IO pins for SWD interface
    _SetSWPinsIdle;

    // Select the Serial Wire Debug Port
    // Skip this switch sequence if the device does not have the swj_dp port
    // Serial Wire + JTAG
    SW_ShiftReset();
    SW_ShiftByteOut(0x9E);
    SW_ShiftByteOut(0xE7);

    // Reset the line and return the 32-bit ID code
    rtn = SWD_LineReset();
    //SendLongToHost(io_word.U32);

    return rtn;
}

//-----------------------------------------------------------------------------
// (0x30) SWD_Disconnect
//-----------------------------------------------------------------------------
//
// Switches the debug interface to JTAG communication and disconnects pins.
//
// Returns:
//    1. HOST_COMMAND_OK
//
STATUS SWD_Disconnect(void)
{
    // Initialize IO pins for SWD interface
    _SetSWPinsIdle;

    // Select the JTAG Debug Port
    // Skip this switch sequence if the device does not have the swj_dp port
    // Serial Wire + JTAG
    SW_ShiftReset();
    SW_ShiftByteOut(0x3C);
    SW_ShiftByteOut(0xE7);

    // Release debug interface pins except nSRST
    _ResetDebugPins;

    return HOST_COMMAND_OK;
}

//-----------------------------------------------------------------------------
// (0x31) SWD_LineReset
//-----------------------------------------------------------------------------
//
// Performs a line reset on the Serial Wire interface.
//
// Returns:
//    1. Response code.
//
STATUS SWD_LineReset(void)
{
    U8 ack;

    // Complete SWD reset sequence (50 cycles high followed by 2 or more idle cycles)
    SW_ShiftReset();
    SW_ShiftByteOut(0);

    // Now read the DPIDR register to move the SWD out of reset
    ack = SW_ShiftPacket(SW_IDCODE_RD, 1);
    SW_ShiftByteOut(0);

    return SW_Response(ack);
}

//-----------------------------------------------------------------------------
// (0x32) SWD_ClearErrors
//-----------------------------------------------------------------------------
//
// Clears all the error/sticky bits in the DP Control/Status register.
//
// Returns:
//  1-4. DP_CSR - DP_CTRLSTAT register value before the clear (32-bit, LE).
//  5-8. DP_CSR - DP_CTRLSTAT register value after the clear (32-bit, LE).
//    9. Response code.
//
STATUS SWD_ClearErrors(void)
{
    U8 ack;

    // First read the DP-CSR register and send the value to the host.
    SW_ShiftPacket(SW_CTRLSTAT_RD, 1);
    //SendLongToHost(io_word.U32);

    // Clear all error/sticky bits by writing to the abort register.
    io_word.U32 = 0x1E;
    SW_ShiftPacket(SW_ABORT_WR, 1);

    // Read the DP-CSR register again and send the results to the host.
    ack = SW_ShiftPacket(SW_CTRLSTAT_RD, 1);
    SW_ShiftByteOut(0);
    //SendLongToHost(io_word.U32);

    return SW_Response(ack);
}

//-----------------------------------------------------------------------------
// (0x34) SWD_DAP_Move
//-----------------------------------------------------------------------------
//
// Reads or writes one Debug/Access Port address one or more times.
//
// Parameters:
//    1. Count - Number of words to transfer minus one (i.e. 0=move 1 word).
//    2. DAP_Addr - The DAP register address to transfer one or more times.
//  3-n. Words[] - Array of 32-bit LE words (write transfers only).
//
// Returns:
//  1-n. Words[] - Array of 32-bit LE words (read transfers only).
//  n+1. Response code.
//
// Uses:
//    ack_error - Resets error accumulator.
//
STATUS SWD_DAP_Move(U8 cnt, U8 dap, U32 * transfer_data)
{
    // Reset global error accumulator
    ack_error = SW_ACK_OK;

    // Determine if this is a read or write transfer
    if (dap & DAP_CMD_RnW)
    {
        // Perform the requested number of reads
        SW_DAP_Read(cnt, dap, transfer_data);
    }
    else
    {
        SW_DAP_Write(cnt, dap, transfer_data, TRUE);
    }

    // Finish with idle cycles
    SW_ShiftByteOut(0);

    // Return the accumulated error result
    return SW_Response(ack_error);
}

//-----------------------------------------------------------------------------
// SWD Helper Functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// SW_Response
//-----------------------------------------------------------------------------
//
// Converts SWD acknowledge value into a host response code.
//
// Parameters:
//    SW_Ack - 3-bit SWD acknowledge code.
//
// Returns:
//    8-bit host response code.
//
STATUS SW_Response(U8 SW_Ack)
{
    switch (SW_Ack)
    {
    case SW_ACK_OK:     return HOST_COMMAND_OK;
    case SW_ACK_WAIT:   return HOST_AP_TIMEOUT;
    case SW_ACK_FAULT:  return HOST_ACK_FAULT;
    default:            return HOST_WIRE_ERROR;
    }
}

//-----------------------------------------------------------------------------
// SW_DAP_Read
//-----------------------------------------------------------------------------
//
// Does one or more reads of one Debug/Access Port register and returns each
// 32-bit word to the host.
//
// Parameters:
//    cnt - Number of words to read minus one (i.e. 0=read 1 word).
//    DAP_Addr - The DAP register address to read one or more times.
//
void SW_DAP_Read(U8 cnt, U8 DAP_Addr, U32 * read_data)
{
    U8 req;

    // Format the packet request header
    req = SW_Request(DAP_Addr);

    // Shift the first packet and if DP access, send the results
    SW_ShiftPacket(req, 0);
    if (!(req & SW_REQ_APnDP))
    {
        *read_data = io_word.U32;
        read_data++;
    }

    // Perform the requested number of reads
    for (; cnt != 0; cnt--)
    {
        SW_ShiftPacket(req, 0);
        *read_data = io_word.U32;
        read_data++;
    }

    // For AP access, get and send results of the last read
    if (req & SW_REQ_APnDP)
    {
        SW_ShiftPacket(SW_RDBUFF_RD, 0);
        *read_data = io_word.U32;
        read_data++;
    }
}

//-----------------------------------------------------------------------------
// SW_DAP_Write
//-----------------------------------------------------------------------------
//
// Does one or more writes to one Debug/Access Port register getting each
// 32-bit word from the host.
//
// Parameters:
//    cnt - Number of words to write minus one (ie 0=write 1 word).
//    DAP_Addr - The DAP register address to write one or more times.
//    final - True if this is the final transfer of a move sequence.
//
void SW_DAP_Write(U8 cnt, U8 DAP_Addr, U32 * write_data, BOOL final)
{
    U8 req;

    // Format the packet request header
    req = SW_Request(DAP_Addr);

    // Perform the requested number of writes
    do
    {
        io_word.U32 = *write_data;
        write_data++;
#if 0
        // Clear the upper half word for 16-bit packed writes
           io_word.U16[MSB] = 0;

        // For packed transfers, write 16-bits at a time
        if (DAP_Addr & DAP_CMD_PACKED)
        {
            SW_ShiftPacket(req, 0);
            io_word.U32 = *write_data;
            write_data++;

            // Clear the upper half word for 16-bit packed writes
               io_word.U16[MSB] = 0;
        }
        else
        {
            io_word.U16[MSB] = (U16) *write_data;
            write_data++;
        }
#endif
        SW_ShiftPacket(req, 0);
    }
    while (cnt-- != 0);

    // For AP access, check results of last write (use default retry count
    // because previous write may need time to complete)
    if (final && (req & SW_REQ_APnDP))
    {
        SW_ShiftPacket(SW_RDBUFF_RD, 0);
    }
}

//-----------------------------------------------------------------------------
// SW_Request
//-----------------------------------------------------------------------------
//
// Converts DAP address into SWD packet request value.
//
// Parameters:
//    DAP_Addr - 4-bit DAP address (A3:A2:RnW:APnDP).
//
// Returns:
//    Complete 8-bit packet request value. Includes parity, start, etc.
//
U8 SW_Request(U8 DAP_Addr)
{
    U8 req;

    // Convert the DAP address into a SWD packet request value
    req = DAP_Addr & DAP_CMD_MASK;      // mask off the bank select bits
    req = req | even_parity[req];       // compute and add parity bit
    req = req << 1;                     // move address/parity bits
    req = req | SW_REQ_PARK_START;      // add start and park bits
    return req;
}

//-----------------------------------------------------------------------------
// SW_CalcDataParity
//-----------------------------------------------------------------------------
//
// Calculates even parity over the 32-bit value in io_word.U32. Contents of
// io_word are not changed.
//
// Returns:
//    1-bit even parity.
//
// Uses:
//    io_word - Holds 32-bit value to compute parity on.
//
bit SW_CalcDataParity(void)
{
    U8 parity;

    // Calculate column parity, reducing down to 4 columns
    parity  = io_word.U8[b0];
    parity ^= io_word.U8[b1];
    parity ^= io_word.U8[b2];
    parity ^= io_word.U8[b3];
    parity ^= parity >> 4;

    // Use lookup table to get parity on 4 remaining bits. The cast (bit)
    // converts any non-zero value to 1.
    return (bit)even_parity[parity & 0xF];
}

//-----------------------------------------------------------------------------
// SW_ShiftReset
//-----------------------------------------------------------------------------
//
// Puts the SWD into the reset state by clocking 64 times with SWDIO high.
// Leaves SWDIO an output and high.
//
void SW_ShiftReset(void)
{
    U8 i;

    // Drive SWDIO high
    SWDIO_Out = 1;
    _SetSWDIOasOutput;

    // Complete 64 SWCLK cycles
    for (i = 64; i != 0; i--)
    {
        _StrobeSWCLK;
    }
}

//-----------------------------------------------------------------------------
// SW_ShiftPacket
//-----------------------------------------------------------------------------
//
// Completes one serial wire packet transfer (read or write). Expects SWDIO to
// be an output on entry.
//
// Parameters:
//    request - Complete 8-bit packet request value. Includes parity, start, etc.
//    retry - Number of times to try the request while the target ack is WAIT.
//       0 = use the system default retry count
//       n = try the request upto n times
//
// Returns:
//    3-bit SWD acknowledge code.
//    Leaves SWDIO an output and low.
//
// Uses:
//    ack_error - Updated if there was a transfer error.
//    io_byte - Used for all transfers.
//    io_word - On entry, holds the 32-bit word data to transfer on writes.
//              On exit, holds the 32-bit word data transfered on reads.
//
U8 SW_ShiftPacket(U8 request, U8 retry)
{
    U8 ack, limit, i;

    // If retry parameter is zero, use the default value instead
    if (retry == 0)
    {
        retry = DAP_RETRY_COUNT;
    }
    limit = retry;

    // While waiting, do request phase (8-bit request, turnaround, 3-bit ack)
    do
    {
        // Turnaround or idle cycle, makes or keeps SWDIO an output
        SWDIO_Out = 0; 
_SetSWDIOasOutput; // SWD = 0
_StrobeSWCLK; // SWC = 1, SWC = 0 -- IDLE State
// Shift out the 8-bit packet request SW_ShiftByteOut(request); // Turnaround cycle makes SWDIO an input _SetSWDIOasInput; // Float, Pull Up
_StrobeSWCLK; // SWC = 1, SWC = 0
// Shift in the 3-bit acknowledge response io_byte = 0; iob_0 = SWDIO_In; _StrobeSWCLK; iob_1 = SWDIO_In; _StrobeSWCLK; iob_2 = SWDIO_In; _StrobeSWCLK; ack = io_byte; // Check if we need to retry the request if ((ack == SW_ACK_WAIT) && --retry) { // Delay an increasing amount with each retry for (i=retry; i < limit; i++); } else { break; // Request phase complete (or timeout) } } while (TRUE); // If the request was accepted, do the data transfer phase (turnaround if // writing, 32-bit data, and parity) if (ack == SW_ACK_OK) { if (request & SW_REQ_RnW) { // Swap endian order while shifting in 32-bits of data io_word.U8[b0] = SW_ShiftByteIn(); io_word.U8[b1] = SW_ShiftByteIn(); io_word.U8[b2] = SW_ShiftByteIn(); io_word.U8[b3] = SW_ShiftByteIn(); // Shift in the parity bit iob_0 = SWDIO_In; _StrobeSWCLK; // Check for parity error if (iob_0 ^ SW_CalcDataParity()) { ack = SW_ACK_PARITY_ERR; } } else { // Turnaround cycle makes SWDIO an output _SetSWDIOasOutput; _StrobeSWCLK; // Swap endian order while shifting out 32-bits of data SW_ShiftByteOut(io_word.U8[b0]); SW_ShiftByteOut(io_word.U8[b1]); SW_ShiftByteOut(io_word.U8[b2]); SW_ShiftByteOut(io_word.U8[b3]); // Shift out the parity bit SWDIO_Out = SW_CalcDataParity(); _StrobeSWCLK; } } // TODO: Add error (FAULT, line, parity) handling here? RESEND on parity error? // Turnaround or idle cycle, always leave SWDIO an output SWDIO_Out = 0; _SetSWDIOasOutput; _StrobeSWCLK; // Update the global error accumulator if there was an error if (ack != SW_ACK_OK) { ack_error = ack; } return ack; } //----------------------------------------------------------------------------- // SW_ShiftByteOut //----------------------------------------------------------------------------- // // Shifts an 8-bit byte out the SWDIO pin. // // Parameters: // byte - The 8-bit byte to shift out on SWDIO. // // Uses: // io_byte - Holds byte as it is shifted out. // #pragma OT(8, SPEED) void SW_ShiftByteOut(U8 byte) { // Make sure SWDIO is an output _SetSWDIOasOutput; // Copy data to bit addressable location io_byte = byte; // Shift 8-bits out on SWDIO SWDIO_Out = iob_0; _StrobeSWCLK; SWDIO_Out = iob_1; _StrobeSWCLK; SWDIO_Out = iob_2; _StrobeSWCLK; SWDIO_Out = iob_3; _StrobeSWCLK; SWDIO_Out = iob_4; _StrobeSWCLK; SWDIO_Out = iob_5; _StrobeSWCLK; SWDIO_Out = iob_6; _StrobeSWCLK; SWDIO_Out = iob_7; _StrobeSWCLK; } //----------------------------------------------------------------------------- // SW_ShiftByteIn //----------------------------------------------------------------------------- // // Shifts an 8-bit byte in from the SWDIO pin. // // Returns: // 8-bit byte value shifted in on SWDIO. // // Uses: // io_byte - Holds byte as it is shifted in. // #pragma OT(8, SPEED) U8 SW_ShiftByteIn(void) { // Make sure SWDIO is an input _SetSWDIOasInput; // Shift 8-bits in on SWDIO iob_0 = SWDIO_In; _StrobeSWCLK; iob_1 = SWDIO_In; _StrobeSWCLK; iob_2 = SWDIO_In; _StrobeSWCLK; iob_3 = SWDIO_In; _StrobeSWCLK; iob_4 = SWDIO_In; _StrobeSWCLK; iob_5 = SWDIO_In; _StrobeSWCLK; iob_6 = SWDIO_In; _StrobeSWCLK; iob_7 = SWDIO_In; _StrobeSWCLK; // Return the byte that was shifted in return io_byte; }

 

posted @ 2015-08-22 21:10  IAmAProgrammer  阅读(2108)  评论(0编辑  收藏  举报