diff --git a/minichlink/Makefile b/minichlink/Makefile index dc6c8cc949c0dbbe024a5b31ccadd512d70b9455..a60c9f9b4f4ff6706cbab48dfec14641a1495561 100644 --- a/minichlink/Makefile +++ b/minichlink/Makefile @@ -23,6 +23,9 @@ else LIBINCLUDES:=$(shell pkg-config --libs-only-L libusb-1.0) INCS:=$(INCLUDES) $(LIBINCLUDES) endif + + # Until it's supported on Windows, add the Ardulink file here. + C_S:=$(C_S) ardulink.c endif all : $(TOOLS) diff --git a/minichlink/ardulink.c b/minichlink/ardulink.c new file mode 100644 index 0000000000000000000000000000000000000000..5da4d10640b31c44aaa2c8651182aa14aedb8a08 --- /dev/null +++ b/minichlink/ardulink.c @@ -0,0 +1,186 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <errno.h> + +#include "minichlink.h" + +void * TryInit_Ardulink(void); + +static int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command); +static int ArdulinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp); +static int ArdulinkFlushLLCommands(void * dev); +static int ArdulinkExit(void * dev); +static int ArdulinkTargetReset(void * dev, int reset); + + +typedef struct { + struct ProgrammerStructBase psb; + int fd; +} ardulink_ctx_t; + +int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command) +{ + uint8_t buf[6]; + buf[0] = 'w'; + buf[1] = reg_7_bit; + + //fprintf(stderr, "WriteReg32: 0x%02x = 0x%08x\n", reg_7_bit, command); + + buf[2] = command & 0xff; + buf[3] = (command >> 8) & 0xff; + buf[4] = (command >> 16) & 0xff; + buf[5] = (command >> 24) & 0xff; + + if (write(((ardulink_ctx_t*)dev)->fd, buf, 6) == -1) + return -errno; + + if (read(((ardulink_ctx_t*)dev)->fd, buf, 1) == -1) + return -errno; + + return buf[0] == '+' ? 0 : -EPROTO; +} + +int ArdulinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp) +{ + uint8_t buf[4]; + buf[0] = 'r'; + buf[1] = reg_7_bit; + + if (write(((ardulink_ctx_t*)dev)->fd, buf, 2) == -1) + return -errno; + + if (read(((ardulink_ctx_t*)dev)->fd, buf, 4) == -1) + return -errno; + + *commandresp = (uint32_t)buf[0] | (uint32_t)buf[1] << 8 | \ + (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24; + + //fprintf(stderr, "ReadReg32: 0x%02x = 0x%08x\n", reg_7_bit, *commandresp); + + return 0; +} + +int ArdulinkFlushLLCommands(void * dev) +{ + return 0; +} + +int ArdulinkExit(void * dev) +{ + close(((ardulink_ctx_t*)dev)->fd); + free(dev); + return 0; +} + +int ArdulinkTargetReset(void * dev, int reset) { + char c; + + fprintf(stderr, "Ardulink: target reset %d\n", reset); + + // Assert reset. + c = reset ? 'a' : 'A'; + if (write(((ardulink_ctx_t*)dev)->fd, &c, 1) == -1) + return -errno; + + if (read(((ardulink_ctx_t*)dev)->fd, &c, 1) == -1) + return -errno; + + if (c != '+') + return -EPROTO; + + usleep(20000); + return 0; +} + +void * TryInit_Ardulink(void) +{ + ardulink_ctx_t *ctx; + struct termios attr; + char first; + int argp = TIOCM_DTR; + + if (!(ctx = calloc(sizeof(ardulink_ctx_t), 1))) { + perror("calloc"); + return NULL; + } + + if ((ctx->fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY)) == -1) { + perror("open"); + return NULL; + } + + if (tcgetattr(ctx->fd, &attr) == -1) { + perror("tcgetattr"); + return NULL; + } + + cfmakeraw(&attr); + cfsetspeed(&attr, 115200); + + if (tcsetattr(ctx->fd, TCSANOW, &attr) == -1) { + perror("tcsetattr"); + return NULL; + } + + // Arduino DTR reset. + if (ioctl(ctx->fd, TIOCMBIC, &argp) == -1) { + perror("ioctl"); + return NULL; + } + + if (tcdrain(ctx->fd) == -1) { + perror("tcdrain"); + return NULL; + } + + if (ioctl(ctx->fd, TIOCMBIS, &argp) == -1) { + perror("ioctl"); + return NULL; + } + + if (tcdrain(ctx->fd) == -1) { + perror("tcdrain"); + return NULL; + } + + // Flush anything that might be in the RX buffer, we need the sync char. + if (tcflush(ctx->fd, TCIFLUSH) == -1) { + perror("tcflush"); + return NULL; + } + + // Let the bootloader do its thing. + sleep(3); + + if (read(ctx->fd, &first, 1) == -1) { + perror("read"); + return NULL; + } + + if (first != '!') { + fprintf(stderr, "Ardulink: not the sync character.\n"); + return NULL; + } + + if (ArdulinkTargetReset(ctx, 1) != 0) { + fprintf(stderr, "Ardulink: target reset failed.\n"); + return NULL; + } + + fprintf(stderr, "Ardulink: synced.\n"); + + MCF.WriteReg32 = ArdulinkWriteReg32; + MCF.ReadReg32 = ArdulinkReadReg32; + MCF.FlushLLCommands = ArdulinkFlushLLCommands; + MCF.Exit = ArdulinkExit; + MCF.TargetReset = ArdulinkTargetReset; + + return ctx; +} diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c index 8c3e5ecd3b7c5353a9713945985c4be9931cff0e..980cf9329794c50f03fd179f08fe414308277473 100644 --- a/minichlink/minichlink.c +++ b/minichlink/minichlink.c @@ -12,6 +12,7 @@ #include "../ch32v003fun/ch32v003fun.h" #if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) +#define DISABLE_ARDULINK void Sleep(uint32_t dwMilliseconds); #else #include <unistd.h> @@ -41,6 +42,15 @@ void * MiniCHLinkInitAsDLL( struct MiniChlinkFunctions ** MCFO ) { fprintf( stderr, "Found NHC-Link042 Programmer\n" ); } + +#ifndef DISABLE_ARDULINK + else if ((dev = TryInit_Ardulink())) + { + fprintf( stderr, "Found Ardulink Programmer\n" ); + } +#else + #warning Ardulink not yet supported on Windows. +#endif else { fprintf( stderr, "Error: Could not initialize any supported programmers\n" ); @@ -1656,6 +1666,8 @@ static int DefaultHaltMode( void * dev, int mode ) } #endif + MCF.TargetReset(dev, 0); + iss->processor_in_mode = mode; return 0; } diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h index 131a433c4183202c4ac8787c30ae89f13b79ea20..6a974ff230635fa24a2a769512d61c356dc3013c 100644 --- a/minichlink/minichlink.h +++ b/minichlink/minichlink.h @@ -78,6 +78,8 @@ struct MiniChlinkFunctions int (*WriteByte)( void * dev, uint32_t address_to_write, uint8_t data ); int (*ReadByte)( void * dev, uint32_t address_to_read, uint8_t * data ); + + int (*TargetReset)( void * dev, int reset ); }; /** If you are writing a driver, the minimal number of functions you can implement are: @@ -151,6 +153,7 @@ extern struct MiniChlinkFunctions MCF; void * TryInit_WCHLinkE(); void * TryInit_ESP32S2CHFUN(); void * TryInit_NHCLink042(void); +void * TryInit_Ardulink(); // Returns 0 if ok, populated, 1 if not populated. int SetupAutomaticHighLevelFunctions( void * dev );