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