// Listens on parallel port for MSIO communication with ATMEL. // // Compile with: // gcc -Wall -o msio_host msio_host.c // // Controlling the parallel port is copy&paster from on avrdude source // code. // // TODO: // - share code with avrdude instead of copy&paste to support: // - other adapters. However, only adaptors on which we can // control all lines are suitable (good: par, bad: stk500). // - other OSes // // R. Nijlunsing // License: GPL (same as AVRdude) //////////////////// Configuration // Device of parallel port #define PARDEV "/dev/parport0" //// Pin layout. Pins on parallel port just like the DAPA/PPI in //// /etc/avrdude.conf . // Reset pin. If undefined, does not reset ATMEL at start. #define RESET 16 #define SCK 1 #define MOSI 2 #define MISO 11 //////////////////// End of configuration #include #include #include #include #include #include #include #include #include #include #include #define OBSOLETE__IOW _IOW #define PPISDATA PPWDATA #define PPIGDATA PPRDATA #define PPISCTRL PPWCONTROL #define PPIGCTRL PPRCONTROL #define PPISSTATUS PPWSTATUS #define PPIGSTATUS PPRSTATUS static char * progname = "msio_host"; enum { PPIDATA, PPICTRL, PPISTATUS }; enum { PPI_READ, PPI_WRITE, PPI_SHADOWREAD }; #define ppi_claim(fd) \ if (ioctl(fd, PPCLAIM) < 0) { \ fprintf(stderr, "%s: can't claim device: %s\n\n", \ progname, strerror(errno)); \ close(fd); \ exit(1); \ } #define ppi_release(fd) \ if (ioctl(fd, PPRELEASE) < 0) { \ fprintf(stderr, "%s: can't release device: %s\n\n", \ progname, strerror(errno)); \ exit(1); \ } int ppi_shadow_access(int fd, int reg, unsigned char *v, unsigned char action) { static unsigned char shadow[3]; int shadow_num; unsigned long set, get; int rc = 0; switch (reg) { case PPIDATA: set = PPISDATA; get = PPIGDATA; shadow_num = 0; break; case PPICTRL: set = PPISCTRL; get = PPIGCTRL; shadow_num = 1; break; case PPISTATUS: set = PPISSTATUS; get = PPIGSTATUS; shadow_num = 2; break; default: fprintf(stderr, "%s: avr_set(): invalid register=%d\n", progname, reg); return -1; break; } switch (action) { case PPI_SHADOWREAD: *v = shadow[shadow_num]; break; case PPI_READ: rc = ioctl(fd, get, v); shadow[shadow_num]=*v; break; case PPI_WRITE: shadow[shadow_num]=*v; rc = ioctl(fd, set, v); break; } return rc; } /* * set the indicated bit of the specified register. */ int ppi_set(int fd, int reg, int bit) { unsigned char v; int rc; rc = ppi_shadow_access(fd, reg, &v, PPI_SHADOWREAD); v |= bit; rc |= ppi_shadow_access(fd, reg, &v, PPI_WRITE); if (rc) return -1; return 0; } /* * clear the indicated bit of the specified register. */ int ppi_clr(int fd, int reg, int bit) { unsigned char v; int rc; rc = ppi_shadow_access(fd, reg, &v, PPI_SHADOWREAD); v &= ~bit; rc |= ppi_shadow_access(fd, reg, &v, PPI_WRITE); if (rc) return -1; return 0; } /* * get the indicated bit of the specified register. */ int ppi_get(int fd, int reg, int bit) { unsigned char v; int rc; rc = ppi_shadow_access(fd, reg, &v, PPI_READ); v &= bit; if (rc) return -1; return v; /* v == bit */ } /* * toggle the indicated bit of the specified register. */ int ppi_toggle(int fd, int reg, int bit) { unsigned char v; int rc; rc = ppi_shadow_access(fd, reg, &v, PPI_SHADOWREAD); v ^= bit; rc |= ppi_shadow_access(fd, reg, &v, PPI_WRITE); if (rc) return -1; return 0; } /* * get all bits of the specified register. */ int ppi_getall(int fd, int reg) { unsigned char v; int rc; rc = ppi_shadow_access(fd, reg, &v, PPI_READ); if (rc) return -1; return v; /* v == bit */ } /* * set all bits of the specified register to val. */ int ppi_setall(int fd, int reg, int val) { unsigned char v; int rc; v = val; rc = ppi_shadow_access(fd, reg, &v, PPI_WRITE); if (rc) return -1; return 0; } int ppi_open(char * port) { int fd; unsigned char v; fd = open(port, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: can't open device \"%s\": %s\n", progname, port, strerror(errno)); return -1; } /* * Initialize shadow registers */ ppi_claim(fd); ppi_shadow_access (fd, PPIDATA, &v, PPI_READ); ppi_shadow_access (fd, PPICTRL, &v, PPI_READ); ppi_shadow_access (fd, PPISTATUS, &v, PPI_READ); return fd; } void ppi_close(int fd) { ppi_release(fd); close(fd); } enum { PPI_AVR_VCC=1, PPI_AVR_BUFF, PIN_AVR_RESET, PIN_AVR_SCK, PIN_AVR_MOSI, PIN_AVR_MISO, PIN_LED_ERR, PIN_LED_RDY, PIN_LED_PGM, PIN_LED_VFY, N_PINS }; #define PIN_INVERSE 0x80 /* flag for inverted pin in serbb */ #define PIN_MASK 0x7f struct ppipins_t { int pin; int reg; int bit; int inverted; }; struct ppipins_t ppipins[] = { { 1, PPICTRL, 0x01, 1 }, { 2, PPIDATA, 0x01, 0 }, { 3, PPIDATA, 0x02, 0 }, { 4, PPIDATA, 0x04, 0 }, { 5, PPIDATA, 0x08, 0 }, { 6, PPIDATA, 0x10, 0 }, { 7, PPIDATA, 0x20, 0 }, { 8, PPIDATA, 0x40, 0 }, { 9, PPIDATA, 0x80, 0 }, { 10, PPISTATUS, 0x40, 0 }, { 11, PPISTATUS, 0x80, 1 }, { 12, PPISTATUS, 0x20, 0 }, { 13, PPISTATUS, 0x10, 0 }, { 14, PPICTRL, 0x02, 1 }, { 15, PPISTATUS, 0x08, 0 }, { 16, PPICTRL, 0x04, 0 }, { 17, PPICTRL, 0x08, 1 } }; int par_setpin(int fd, int pin, int value) { pin &= PIN_MASK; if (pin < 1 || pin > 17) return -1; pin--; if (ppipins[pin].inverted) value = !value; if (value) ppi_set(fd, ppipins[pin].reg, ppipins[pin].bit); else ppi_clr(fd, ppipins[pin].reg, ppipins[pin].bit); return 0; } int par_getpin(int fd, int pin) { int value; pin &= PIN_MASK; if (pin < 1 || pin > 17) return -1; pin--; value = ppi_get(fd, ppipins[pin].reg, ppipins[pin].bit); if (value) value = 1; if (ppipins[pin].inverted) value = !value; return value; } //////////////////// MSIO specific part: static int fd; void reset_atmel(void) { #ifdef RESET printf("Resetting ATMEL...\n"); par_setpin(fd, RESET, 0); usleep(100000); par_setpin(fd, RESET, 1); usleep(500000); #endif } void set_sck(int enabled) { par_setpin(fd, SCK, enabled); } void set_mosi(int enabled) { par_setpin(fd, MOSI, enabled); } int get_miso(void) { return par_getpin(fd, MISO); } // Time to wait between the bits. void wait_bit(void) { // Fast, but host PC speed dependant and unreliable: // int i; for (i = 0; i < 5000; i++) ; // Slow (same baud as HZ of Linux kernel) but more reliable: usleep(0); } unsigned char receive_byte(void) { unsigned char res = 0; int i; int sck = 1; // We're not responding yet set_sck(sck); // Wait for attention-needed signal from ATMEL. Poll at a relative // low frequency. // printf("Waiting for attention...\n"); while (get_miso()) usleep(1); for (i = 0; i < 8; i++) { int bit; sck ^= 1; set_sck(sck); wait_bit(); bit = get_miso(); // printf("%i", bit); fflush(stdout); res += (1 << i) * bit; } // printf(" %i\n", res); // Send stop bit set_sck(0); wait_bit(); set_sck(1); return res; } unsigned int gettime(void) { struct timeval t; gettimeofday(&t, NULL); return (t.tv_sec * 1000000) + t.tv_usec; } int main(void) { fd = ppi_open(PARDEV); set_sck(1); // State idle. reset_atmel(); while (1) { unsigned int start, elapsed; char c; start = gettime(); c = receive_byte(); printf("%c", c); fflush(stdout); // elapsed = gettime() - start; printf("%i\n", elapsed); } ppi_close(fd); return 0; }