Programujeme jednočipy/VnParProg c code

  1. include <avr/io.h>
/*
 * Atmega8 reset
 * Resets the fuses of a blocked atmega8 MCU using high-voltage parallel programming.
 * This operation enables standard serial programming, which uses the internal oscillator.
 *
 * Needs two MCUs: 	the doctor (with default fuses) using this firmware
 * 			the patient, which has wrong fuses
 * 			(for example RSTDSBL set and/or wrong clock source)
 *
 * This program is published under GFDL, being a part of the
 * http://cs.wikibooks.org/w/index.php?title=Programujeme_jednočipy
 * textbook. See this textbook for electrical schematic and usage.
 * Written by Filip Dominec, 2009
 */

// Pins definition to comply with dsheet 
// following pins shall be connected 1:1 from doctor to patient
#define DATA0 	_BV(PB0)
#define DATA1 	_BV(PB1)
#define DATA2 	_BV(PB2)
#define DATA3 	_BV(PB3)
#define DATA4 	_BV(PB4)
#define DATA5 	_BV(PB5)
#define XTAL1	_BV(PB6)

#define DATA6 	_BV(PC0)
#define DATA7 	_BV(PC1)
#define BS2	_BV(PC2)

#define RDY	_BV(PD1)
#define OE 	_BV(PD2)
#define WR 	_BV(PD3)
#define BS1	_BV(PD4)
#define XA0	_BV(PD5)
#define XA1	_BV(PD6)
#define PAGEL	_BV(PD7)
// following pin turns the patient on and connects +12 V to its RESET pin
// use a simple circuit with two transistors (PNP/NPN) to switch this high voltage
#define RESET	_BV(PC3)


void delay(unsigned int num) 
{
  unsigned int i,j;
  for (j = 0; j < 1000; j++)
    for (i = 0; i < num; i++)
      ;
}
 
void blinkall() 
{
	PORTB |= (XTAL1 | DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5);
	PORTC |= (DATA6 | DATA7 | BS2 | RESET);
	PORTD |= (RDY   | OE    | WR    | BS1   | XA0   | XA1   | PAGEL);
	delay(100);
	PORTB &= 255^(XTAL1 | DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5);
	PORTC &= 255^(DATA6 | DATA7 | BS2 | RESET);
	PORTD &= 255^(RDY   | OE    | WR    | BS1   | XA0   | XA1   | PAGEL);
	delay(100);
	// FIXME: PC0 = 0, 
	//while (1) { blinkall(); }
}



 
void initialize_pins()
{
	// DDR = "Data Direction Register" (note that RDY is input)
	DDRB = (XTAL1 | DATA0 | DATA1 	| DATA2 | DATA3 | DATA4 | DATA5);
	DDRC = (DATA6 | DATA7 | BS2 	| RESET);
	DDRD = (	OE    | WR    	| BS1   | XA0   | XA1   | PAGEL);

	// initialize the outputs 
	PORTB = (0);
	PORTC = (0);
	PORTD = (OE 	| WR); 		// PAGEL, XA1, XA0, BS1 must be down, but OE, WR are inverted
	delay(200);

	// reset the patient
	PORTC |= (RESET); 
	delay(20);	
	//PORTD = (PAGEL | XA1 | XA0 | BS1);  WTF?
}
 
void chip_erase()
{
	// set command mode
	PORTD &= ~(XA0);
	PORTD |= (XA1);

	PORTD &= ~(BS1);

	// set data to "1000 0000"
	PORTB &= ~(DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5);
	PORTC &= ~(DATA6);
	PORTC |= (DATA7);

	// XTAL1 strobe
	delay(20); PORTB |= (XTAL1); delay(10); PORTB &= ~(XTAL1); delay(20);  
	
	// WR strobe
	delay(20); PORTD &= ~(WR); delay(10); PORTD |= (WR); 

	// wait until ready
	while ((PIND & RDY) == 0) {};
	delay(20);  
}
 
void lfuse_erase()
{
	// set command mode
	PORTD &= ~(XA0);
	PORTD |= (XA1);
	
	// set BS1,2 = 0 (?)
	PORTC &= ~(BS2);
	PORTD &= ~(BS1);

	// "fuse erase" command = set DATA7-0 to "01000000b"
	PORTB &= ~(DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5);
	PORTC &= ~(DATA6 | DATA7);
	PORTC |= (DATA6);

	// XTAL1 strobe
	delay(20); PORTB |= (XTAL1); delay(10); PORTB &= ~(XTAL1); delay(20);  

	
	// set data mode
	PORTD &= ~(XA1);
	PORTD |= (XA0);
	
	// Load Data Low byte. Bit n = “0” programs and bit n = “1” erases the Fuse bit.
	// Default value: lfuse = 0xE1
	PORTB &= ~(DATA1 | DATA2 | DATA3 | DATA4);
	PORTC &= ~(0);
	PORTB |= (DATA0 | DATA5);
	PORTC |= (DATA6 | DATA7);
	
	// XTAL1 strobe
	delay(20); PORTB |= (XTAL1); delay(10); PORTB &= ~(XTAL1); delay(20);  
	
	// set BS1 and BS2 to “0” for lfuse
	PORTC &= ~(BS2);
	PORTD &= ~(BS1);
	
	// WR strobe
	delay(20); PORTD &= ~(WR); delay(10); PORTD |= (WR); delay(20);  
}
 
void hfuse_erase()
{
	// set command mode
	PORTD &= ~(XA0); 	PORTD |= (XA1);
	
	// set BS1,2 = 0 (?)
	PORTC &= ~(BS2);
	PORTD &= ~(BS1);

	// "fuse erase" command = set DATA7-0 to "01000000b"
	PORTB &= ~(DATA0 | DATA1 | DATA2 | DATA3 | DATA4 | DATA5);
	PORTC &= ~(DATA6 | DATA7);
	PORTC |= (DATA6);

	// XTAL1 strobe
	delay(20); PORTB |= (XTAL1); delay(10); PORTB &= ~(XTAL1); delay(20);  


	// set data mode
	PORTD &= ~(XA1);
	PORTD |= (XA0);
	
	// Load Data high byte. Bit n = “0” programs and bit n = “1” erases the Fuse bit.
	// Default value: hfuse = 0xd9
	PORTB |= (DATA0 | DATA3 | DATA4);
	PORTC |= (DATA7);
	PORTB &= ~(DATA1 | DATA2 | DATA5);
	PORTC &= ~(DATA6);
	
	// XTAL1 strobe
	delay(20); PORTB |= (XTAL1); delay(10); PORTB &= ~(XTAL1); delay(20);  

	// set BS1 to “1” and BS2 to “0” for hfuse
	PORTC &= ~(BS2);
	PORTD |= (BS1);
	
	// WR strobe
	delay(20); PORTD &= ~(WR); delay(10); PORTD |= (WR); delay(20);  
}
 
int main (void)
{
	initialize_pins();
	
	// note: now it would be time to perform "chip erase" command, if it was locked 
	// chip_erase();
	
	hfuse_erase();
	lfuse_erase();

	// wait until ready, then turn off RESET
	while ((PIND & RDY) == 0) {};
	delay(500);
	PORTC &= ~(RESET);

	return 0;
}

Tento program (atmega8-fuse-repair.c) zkompilujeme klasickým postupem a nahrajeme jej do zdravé Atmegy8, aniž bychom měnili výchozí nastavení pojistek: