On Mon, Mar 19, 2007 at 03:12:43PM -0600, Suhas Pharkute wrote:
>    Hi,
> 
>    Is there any way to communicate to PC back using ISP when program
>    execution is in place.
> 
>    Or can I write a loop that will look for PC input/output for interaction
> 
>    I know I can read/write a block from ISP but that will need to put
>    Processor in prog mode and will halt the exection, correct?
> 
>    Please help
> 
>    Suhas

I don't know whether this is what you require, but it might help you
(and others).

When I soldered an ATMEGA8 CPU and had it programmed via the parallel
port on Linux, I wanted to know whether it 'worked' and having a way
to send debug output to the PC without having to solder extra wires
besides the absolute minimal ones. Since the parallel ISP lines (MISO
+ MOSI + SCK) were already in place to be able to program the ATMEGA,
it seemed natural to me to have some functions sending data from the
ATMEL to the PC. However, I could not find anything on the net.

So I came up with a very simple to implement protocol to send bytes.

>From msio.h:
// On the ATMEL side:
//   - uses a polling loop which monitors the clock (SCK)
//   - uses MISO to send the data bit by bit
//   - continues after a timeout if no one is listening on the other
//     side.
// On the host PC side:
//   - uses the parallel port (could be extended though)
//   - uses SCK to drive the clock to indicate the ATMEL we're ready
//     to receive the next bit.

Usage:
   - On the host PC, compile msio_host.c:
     gcc -Wall -o msio_host msio_host.c
   - For the ATMEGA8, compile msio.c together with your application.
   - Test it by doing in your main:
      #include "msio.h"
      msioInit();
      msioSendString("Hello world!\n");
   - program your ATMEGA the normal way with avrdude
   - start msio_host on the PC.
     This will reset the ATMEGA, which will start the program uploaded
     and will wait from output from the ATMEGA.
     
It currently assumes:
   - PB5 == SCK; PB4 == MISO; PB3 == MOSI
     ...which is true for the ATMEGA8. Change all occurances of PB5,
     PB4 and PB3 in msio.c if configuration is different.
   - A parallel port programmer configured as (pins):
     #define RESET 16
     #define SCK 1
     #define MOSI 2
     #define MISO 11
     Change in msio_host.c to your liking.
   - Linux with module 'ppdev' loaded and being able to access
     /dev/parport0 directly.

Good luck.

Regards,
Rutger.

-- 
Rutger Nijlunsing ---------------------------------- eludias ed dse.nl
never attribute to a conspiracy which can be explained by incompetence
----------------------------------------------------------------------
// One way communication from ATMEGA8 to host PC through SCK and MISO
// lines without any addiontional hardware support on the ATMEL side,
// and no device drivers on the host PC side.
//
// Why
//   No additional soldering needed to get (debugging) output,
//   since it uses the same lines as the ISP parallel programmer.
//
// On the ATMEL side:
//   - uses a polling loop which monitors the clock (SCK)
//   - uses MISO to send the data bit by bit
//   - continues after a timeout if no one is listening on the other
//     side.
// On the host PC side:
//   - uses the parallel port (could be extended though)
//   - uses SCK to drive the clock to indicate the ATMEL we're ready
//     to receive the next bit.
//
// The speed is dependant on the speed the host PC toggles the clock
// and whether the ATMEL can keep up. When the ATMEL runs at 1Mhz, I
// get to ~100kbaud with some errors.
//
// TODO
//   - make communication bi-directional instead of only from
//     ATMEL to host PC using MOSI. Easy to do, just use MOSI also.
//   - add parity for error detection?
//   - make CPU configurable by making location of SCK/MISO pins
//     configurable.
//   - Use SPI protocol instead?
//
// (c)2005 R. Nijlunsing <[EMAIL PROTECTED]>
// License: LGPL (see http://www.gnu.org/copyleft/lesser.html)

#include <avr/io.h>
#include <inttypes.h>

// All functions are defined extern to make them easy to include in a
// library. However, for optimisations like skip-unused-functions use
// one source file like 'main.c' which includes:
//    #define extern static
//    #include "msio.c"
// ..and compile with something like:
//    gcc -finline-functions -funit-at-a-time main.c

// See msio.c for explanation of functions.
extern void msioInit(void);
extern uint8_t msioSendByte(uint8_t s);
extern uint8_t msioSendString(char *buffer);
extern uint8_t msioSendUINT8_T(char *fmt, uint8_t val);
// See msio.h for usage.
//
// Protocol:
//   - MISO: data from ATMEL to host PC
//   - SCK: data from host PC to ATMEL
//   - When ATMEL wants to talk, it sets MISO to low.
//   - To ACK this talk, host sets SCK to low.
//   - Now 4 times 2 bits (lowest bits first) are send with:
//     - ATMEL: copy bit to MISO, wait for SCK to become high again.
//     - Host: sets the SCK high. Wait a little.
//     - ATMEL: copy next bit to MISO, wait for SCK to become low.
//     - Host: sets the SCK low. Wait a little.
//   The ATMEL waits max. a fixed number of times. After that, a timeout
//   occurs and continues.
//
// (c)2005 R. Nijlunsing <[EMAIL PROTECTED]>
// License: LGPL (see http://www.gnu.org/copyleft/lesser.html)

#include <avr/io.h>
#include <inttypes.h>

#include "msio.h"

// Timeout (uint16_t). Time to wait for a clock change from the other
// side. When a timeout occurs, the current transmission of byte or
// string is aborted.
#define SCK_TIMEOUT 60000

static void __inline miso_on(void) { PORTB |= _BV(PB4); }
static void __inline miso_off(void) { PORTB &= ~_BV(PB4); }

// Wait for SCK to become 1. Returns 1 iff OK, 0 if timeout.
static uint8_t sck_wait_till_on(void) {
    uint16_t time = 0;
    while (!(PINB & _BV(PB5))) {
	time++;
	if (time == SCK_TIMEOUT) return 0;
    }
    return 1;
}

// Wait for SCK to become 0. Returns 1 iff OK, 0 if timeout.
static uint8_t sck_wait_till_off(void) {
    uint16_t time = 0;
    while (PINB & _BV(PB5)) {
	time++;
	if (time == SCK_TIMEOUT) return 0;
    }
    return 1;
}

// Initialises. Sets the data direction of used lines.
void msioInit(void) {
    // PB5 == SCK; PB4 == MISO; PB3 == MOSI
    DDRB |= _BV(PB4); // MISO: Output
    DDRB &= ~_BV(PB5);  // SCK: Input
    //    DDRB &= ~_BV(PB3);  // MOSI: Input
    // We're not sending:
    miso_on();
}

// Sends a byte.
// Returns 1 iff OK. 0 iff timeout.
uint8_t msioSendByte(uint8_t s) {
    int8_t i;
    // Ask for attention
    miso_off();
    // Send byte 2 bits a time
    for (i = 0; i < 4; i++) {
	if (!sck_wait_till_off())
	    goto msioTimeout;
	s & 1 ? miso_on() : miso_off();
	s >>= 1;
	if (!sck_wait_till_on())
	    goto msioTimeout;
	s & 1 ? miso_on() : miso_off();
	s >>= 1;
    }
    // Stop bit
    if (!sck_wait_till_off())
	goto msioTimeout;
    miso_on();
    if (!sck_wait_till_on())
	goto msioTimeout;
    return 1;
msioTimeout:
    miso_on();
    return 0;
}

// Sends a string.
// Returns 1 iff OK. 0 iff timeout.
uint8_t msioSendString(char *buffer) {
    while (*buffer) {
	if (!msioSendByte(*buffer))
	    return 0;
	buffer++;
    }
    return 1;
}

// Send a formatted integer value 'val' using format string 'fmt'.
// Returns 1 iff OK. 0 iff timeout.
uint8_t msioSendUINT8_T(char *fmt, uint8_t val) {
    char buffer[20];
    sprintf(buffer, fmt, val);
    return msioSendString(buffer);
}
// 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 <[EMAIL PROTECTED]>
// 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 <errno.h>
#include <fcntl.h>
#include <linux/parport.h>
#include <linux/ppdev.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#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;
}
_______________________________________________
AVR-chat mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/avr-chat

Reply via email to