Skip to content

Instantly share code, notes, and snippets.

@bkifft
Created March 9, 2018 19:52
Show Gist options
  • Save bkifft/4953f848fd4fda0b93b42e838dd23b94 to your computer and use it in GitHub Desktop.
Save bkifft/4953f848fd4fda0b93b42e838dd23b94 to your computer and use it in GitHub Desktop.
//raspberry pi based pic 18f2550 programmer
//only meant as a bootstrap (e.g. for http://openprog.altervista.org/OP_eng.html), not for production use
//only bulk erases then writes programm data (including empty areas) and config, does no verification.
//hardcoded to 16bit adresses for program data (search for "//WARNING" to find the location)
//based on rpp by Giorgio Vazzana (http://holdenc.altervista.org/rpp/rpp.html)
/*
* Raspberry Pi PIC Programmer using GPIO connector
* Copyright 2012 Giorgio Vazzana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Compile: gcc -Wall -O rpp.c -o rpp */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
/* GPIO registers address */
#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define BLOCK_SIZE (256)
/* GPIO setup macros. Always use GPIO_IN(x) before using GPIO_OUT(x) or GPIO_ALT(x,y) */
#define GPIO_IN(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define GPIO_OUT(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
#define GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET(g) *(gpio+7) = 1<<(g) /* sets bit which are 1, ignores bit which are 0 */
#define GPIO_CLR(g) *(gpio+10) = 1<<(g) /* clears bit which are 1, ignores bit which are 0 */
#define GPIO_LEV(g) (*(gpio+13) >> (g)) & 0x00000001
/* GPIO <-> PIC connections */
#define PIC_CLK 4 /* Output */
#define PIC_DATA 7 /* Output */
#define PIC_DATAIN 8 /* Input */
#define PIC_MCLR 9 /* Output */
#define DELAY 40 /* microseconds */
// specific for 18f2550
#define PROGSIZE 0x8000
#define EEPROMSIZE 256
#define EEPROMOFFSET 0xF00000
#define CONFIGSIZE 0xE
#define CONFIGOFFSET 0x300000
#define WRITEBUFFERSIZE 32
#define PIC18F2550_ID 0x1240
#define DELAY_BASE 5 //microseconds, 5 taken as a security margin
#define DELAY_P9 DELAY_BASE * 1000
#define DELAY_P10 DELAY_BASE * 100
#define DELAY_P11 DELAY_BASE * 5000
struct picmemory {
uint32_t program_memory_used_cells;
uint32_t program_memory_max_used_address;
uint8_t has_configuration_data;
uint8_t has_eeprom_data;
uint8_t *program_data; /* 8-bit data */
uint8_t *program_filled; /* 1 if this cell is used */
uint8_t *eeprom_data; /* 8-bit data */
uint8_t *eeprom_filled; /* 1 if this cell is used */
uint8_t *config_data;
};
int mem_fd;
void *gpio_map;
volatile uint32_t *gpio;
/* Set up a memory regions to access GPIO */
void setup_io()
{
/* open /dev/mem */
mem_fd = open("/dev/mem", O_RDWR|O_SYNC);
if (mem_fd == -1) {
perror("Cannot open /dev/mem, run as root");
exit(1);
}
/* mmap GPIO */
gpio_map = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE);
if (gpio_map == MAP_FAILED) {
perror("mmap() failed");
exit(1);
}
/* Always use volatile pointer! */
gpio = (volatile uint32_t *)gpio_map;
/* Configure GPIOs */
GPIO_IN(PIC_CLK); /* must use GPIO_IN before we can use GPIO_OUT */
GPIO_OUT(PIC_CLK);
GPIO_IN(PIC_DATA);
GPIO_OUT(PIC_DATA);
GPIO_IN(PIC_DATAIN);
GPIO_IN(PIC_MCLR);
GPIO_OUT(PIC_MCLR);
GPIO_CLR(PIC_CLK);
GPIO_CLR(PIC_DATA);
GPIO_CLR(PIC_DATAIN);
GPIO_CLR(PIC_MCLR);
usleep(DELAY);
}
/* Release GPIO memory region */
void close_io()
{
int ret;
/* munmap GPIO */
ret = munmap(gpio_map, BLOCK_SIZE);
if (ret == -1) {
perror("munmap() failed");
exit(1);
}
/* close /dev/mem */
ret = close(mem_fd);
if (ret == -1) {
perror("Cannot close /dev/mem");
exit(1);
}
}
void free_picmemory(struct picmemory **ppm)
{
free((*ppm)->program_data);
free((*ppm)->program_filled);
free((*ppm)->eeprom_data);
free((*ppm)->eeprom_filled);
free((*ppm)->config_data);
free(*ppm);
}
/* Read a file in Intel HEX 16-bit format and return a pointer to the picmemory
struct on success, or NULL on error */
struct picmemory *read_inhx16(char *infile, int debug)
{
FILE *fp;
int linenum;
char line[256], *ptr;
size_t linelen;
int nread;
uint16_t adress_offset;
uint8_t adress_ok;
uint16_t i;
uint8_t start_code;
uint8_t byte_count;
uint16_t address;
uint8_t record_type;
uint16_t data;
struct picmemory *pm;
fp = fopen(infile, "r");
if (fp == NULL) {
fprintf(stderr, "Error: cannot open source file %s.\n", infile);
return NULL;
}
pm = calloc(1, sizeof(*pm));
if (pm) {
pm->program_data = calloc(PROGSIZE, sizeof(*pm->program_data));
pm->program_filled = calloc(PROGSIZE, sizeof(*pm->program_filled));
pm->eeprom_data = calloc(EEPROMSIZE, sizeof(*pm->eeprom_data));
pm->eeprom_filled = calloc(EEPROMSIZE, sizeof(*pm->eeprom_filled));
pm->config_data = calloc(CONFIGSIZE, sizeof(*pm->config_data));
}
if (!pm || !pm->program_data || !pm->program_filled || !pm->eeprom_data || !pm->eeprom_filled || !pm->config_data) {
fprintf(stderr, "Error: calloc() failed.\n");
free_picmemory(&pm);
return NULL;
}
fprintf(stderr, "Reading hex file...\n");
linenum = 0;
adress_offset = 0;
while (1) {
ptr = fgets(line, 256, fp);
adress_ok = 0;
if (ptr != NULL) {
linenum++;
linelen = strlen(line);
if (debug) {
fprintf(stderr, " line %d (%zd bytes): '", linenum, linelen);
for (i = 0; i < linelen; i++) {
if (line[i] == '\n')
fprintf(stderr, "\\n");
else if (line[i] == '\r')
fprintf(stderr, "\\r");
else
fprintf(stderr, "%c", line[i]);
}
fprintf(stderr, "'\n");
}
start_code = line[0];
if (start_code != ':') {
fprintf(stderr, "Error: invalid start code.\n");
free_picmemory(&pm);
return NULL;
}
nread = sscanf(&line[1], "%2hhx", &byte_count);
if (nread != 1) {
fprintf(stderr, "Error: cannot read byte count.\n");
free_picmemory(&pm);
return NULL;
}
if (debug)
fprintf(stderr, " byte_count = 0x%02X\n", byte_count);
nread = sscanf(&line[3], "%4hx", &address);
if (nread != 1) {
fprintf(stderr, "Error: cannot read address.\n");
free_picmemory(&pm);
return NULL;
}
if (debug)
fprintf(stderr, " address = 0x%04X\n", address);
nread = sscanf(&line[7], "%2hhx", &record_type);
if (nread != 1) {
fprintf(stderr, "Error: cannot read record type.\n");
free_picmemory(&pm);
return NULL;
}
if (debug)
fprintf(stderr, " record_type = 0x%02X (%s)\n", record_type, record_type == 0 ? "data" : (record_type == 1 ? "EOF" : (record_type == 4 ? "adress offset" : "Unknown")));
if (record_type != 0 && record_type != 1 && record_type != 4) {
fprintf(stderr, "Error: unknown record type.\n");
free_picmemory(&pm);
return NULL;
}
if (4 == record_type)
adress_offset = address << 16;
for (i = 0; i < byte_count; i++) {
nread = sscanf(&line[9+2*i], "%2hx", &data);
adress_ok = 0;
if (nread != 1) {
fprintf(stderr, "Error: cannot read data.\n");
free_picmemory(&pm);
return NULL;
}
if (debug)
fprintf(stderr, " data = 0x%02X\n", data);
if (0 == record_type)
{
if (address + adress_offset + i < PROGSIZE)
{
adress_ok = 1;
pm->program_memory_used_cells += 1;
pm->program_memory_max_used_address = address + adress_offset + i;
if (pm->program_filled[address + adress_offset + i] != 0)
fprintf(stderr, " !!data overwrite. adress: 0x%04X old: 0x%02X new: 0x%02X\n", address + adress_offset + i, pm->program_data[address + adress_offset + i], data);
pm->program_data[address + adress_offset + i] = data;
pm->program_filled[address + adress_offset + i] = 1;
}
if (CONFIGOFFSET <= address + adress_offset + i && address + adress_offset + i <= CONFIGOFFSET + CONFIGSIZE)
{
adress_ok = 1;
pm->has_configuration_data = 1;
pm->config_data[address + adress_offset + i - CONFIGOFFSET] = data;
}
if (EEPROMOFFSET <= address + adress_offset + i && address + adress_offset + i <= EEPROMOFFSET + EEPROMSIZE)
{
adress_ok = 1;
pm->has_eeprom_data = 1;
pm->eeprom_data[address + adress_offset + i - EEPROMOFFSET] = data;
pm->eeprom_filled[address + adress_offset + i - EEPROMOFFSET] = 1;
}
if (0 == adress_ok)
{
fprintf(stderr, "Error: adress out of bounds: 0x%08X\n", address + adress_offset + i);
free_picmemory(&pm);
return NULL;
}
}
}
if (debug)
fprintf(stderr, "\n");
if (record_type == 1)
break;
} else {
fprintf(stderr, "Error: unexpected EOF.\n");
free_picmemory(&pm);
return NULL;
}
}
fclose(fp);
return pm;
}
/* Send a 4-bit command to the PIC */
void pic_send_cmd(uint8_t cmd)
{
int i;
for (i = 0; i < 4; i++) {
GPIO_SET(PIC_CLK);
if ((cmd >> i) & 0x01)
GPIO_SET(PIC_DATA);
else
GPIO_CLR(PIC_DATA);
usleep(DELAY); /* Setup time */
GPIO_CLR(PIC_CLK);
usleep(DELAY); /* Hold time */
}
GPIO_CLR(PIC_DATA);
usleep(DELAY);
}
/* Send 16-bit data to the PIC */
void pic_send_data(uint16_t data)
{
int i;
for (i = 0; i < 16; i++) {
GPIO_SET(PIC_CLK);
if ((data >> i) & 0x01)
GPIO_SET(PIC_DATA);
else
GPIO_CLR(PIC_DATA);
usleep(DELAY); /* Setup time */
GPIO_CLR(PIC_CLK);
usleep(DELAY); /* Hold time */
}
GPIO_CLR(PIC_DATA);
usleep(DELAY);
}
void pic_bulk_erase()
{
/* per spec:
4bit-cmd data
0000 0E3C //1
0000 6EF8
0000 0E00
0000 6EF7
0000 0E05 //5
0000 6EF6
1100 3F3F
0000 0E3C
0000 6EF8
0000 0E00 //10
0000 6EF7
0000 0E04
0000 6EF6
1100 8F8F
0000 0000 //15
0000 [sleep 5 ms (5000 microseconds) + 100 microseconds] 0000
*/
GPIO_SET(PIC_MCLR);
usleep(DELAY);
pic_send_cmd(0x00); //1
pic_send_data(0x0E3C);
pic_send_cmd(0x00); //2
pic_send_data(0x6EF8);
pic_send_cmd(0x00); //3
pic_send_data(0x0E00);
pic_send_cmd(0x00); //4
pic_send_data(0x6EF7);
pic_send_cmd(0x00); //5
pic_send_data(0x0E05);
pic_send_cmd(0x00); //6
pic_send_data(0x6EF6);
pic_send_cmd(0x0C); //7
pic_send_data(0x3F3F);
pic_send_cmd(0x00); //8
pic_send_data(0x0E3C);
pic_send_cmd(0x00); //9
pic_send_data(0x6EF8);
pic_send_cmd(0x00); //10
pic_send_data(0x0E00);
pic_send_cmd(0x00); //11
pic_send_data(0x6EF7);
pic_send_cmd(0x00); //12
pic_send_data(0x0E04);
pic_send_cmd(0x00); //13
pic_send_data(0x6EF6);
pic_send_cmd(0x0C); //14
pic_send_data(0x8F8F);
pic_send_cmd(0x00); //15
pic_send_data(0x0000);
pic_send_cmd(0x00);
usleep(DELAY_P10 + DELAY_P11);
pic_send_data(0x0000);
GPIO_CLR(PIC_MCLR);
usleep(DELAY);
}
void nop_p9_p10()
{
GPIO_CLR(PIC_DATA);
GPIO_SET(PIC_CLK); //up1
usleep(DELAY);
GPIO_CLR(PIC_CLK);
usleep(DELAY);
GPIO_SET(PIC_CLK); //up2
usleep(DELAY);
GPIO_CLR(PIC_CLK);
usleep(DELAY);
GPIO_SET(PIC_CLK); //up3
usleep(DELAY);
GPIO_CLR(PIC_CLK);
usleep(DELAY);
GPIO_SET(PIC_CLK);
usleep(DELAY_P9);
GPIO_CLR(PIC_CLK);
usleep(DELAY_P10);
pic_send_data(0x0000);
usleep (DELAY); //doesn't hurt
}
/* Bulk erase the chip, and then write contents of the .hex file to the PIC */
void pic_write(char *infile, int debug)
{
//int error = 0;
struct picmemory *pm;
int adress;
int offset;
pm = read_inhx16(infile, debug);
if (!pm)
return;
/* Bulk erase the chip first */
pic_bulk_erase();
fprintf(stderr, "Writing chip...\n");
GPIO_SET(PIC_MCLR);
usleep(DELAY);
/*writing per spec:
enable writemode:
0000 8EA6 //1
0000 9CA6 //2
set write start adress:
0000 0E[adress21:16] //3
0000 6EF8 //4
0000 0E[adress15:08] //5
0000 6EF7 //6
0000 0E[adress07:01] //7
0000 6EF6 //8
push all but last two program bytes:
loop:
1101 2byteDATA
last two bytes:
1111 2byteDATA
SPECIAL: data low. three regular clocks (up and down). clock high. hold for P9. clock low. hold for P10. send regular data 0x0000 .
*/
for (adress = 0; adress < PROGSIZE; adress += WRITEBUFFERSIZE)
{
fprintf(stderr, "Writing program, adress: 0x%08X ", adress);
pic_send_cmd(0x00); //1
pic_send_data(0x8EA6);
pic_send_cmd(0x00); //2
pic_send_data(0x9CA6);
pic_send_cmd(0x00); //3
pic_send_data(0x0E00); //WARNING: lazy ass hardcoded
pic_send_cmd(0x00); //4
pic_send_data(0x6EF8);
pic_send_cmd(0x00); //5
pic_send_data(0x0E00 | (adress >> 8));
pic_send_cmd(0x00); //6
pic_send_data(0x6EF7);
pic_send_cmd(0x00); //7
pic_send_data(0x0E00 | ( adress & 0x00FF ) );
pic_send_cmd(0x00); //8
pic_send_data(0x6EF6);
for (offset = 0; offset < WRITEBUFFERSIZE - 2 ; offset += 2)
{
fprintf(stderr, "0x%04X ", (pm->program_data[adress + offset] << 8) | pm->program_data[adress + offset + 1]);
pic_send_cmd(0x0D);
pic_send_data( (pm->program_data[adress + offset] << 8) | pm->program_data[adress + offset + 1]);
}
offset += 2;
pic_send_cmd(0x0F);
pic_send_data( (pm->program_data[adress + offset] << 8) | pm->program_data[adress + offset + 1]);
fprintf(stderr, "\n");
nop_p9_p10();
}
////config//////////////////////////////////////////////////////
fprintf(stderr, "Writing config...\n");
for (adress = 0; adress < CONFIGSIZE; adress++)
{
pic_send_cmd(0x00); //1
pic_send_data(0x8EA6);
pic_send_cmd(0x00); //2
pic_send_data(0x8CA6);
pic_send_cmd(0x00); //3
pic_send_data(0x0E30);
pic_send_cmd(0x00); //4
pic_send_data(0x6EF8);
pic_send_cmd(0x00); //5
pic_send_data(0x0E00);
pic_send_cmd(0x00); //6
pic_send_data(0x6EF7);
pic_send_cmd(0x00); //7
pic_send_data(0x0E00 | adress );
pic_send_cmd(0x00); //8
pic_send_data(0x6EF6);
pic_send_cmd(0x0F);
if(!(adress&0x01))
{
fprintf(stderr, "Adress: 0x%08X 0x%04X\n",adress, 0x0000 | pm->config_data[adress]<<8);
pic_send_data(0x0000 | pm->config_data[adress]<<8 );
}
else
{
fprintf(stderr, "Adress: 0x%08X 0x%04X\n",adress, 0x0000 | pm->config_data[adress]);
pic_send_data(0x0000 | pm->config_data[adress] );
}
nop_p9_p10();
}
///end config
GPIO_CLR(PIC_MCLR);
usleep(DELAY);
}
int main(int argc, char *argv[])
{
pic_bulk_erase();
pic_write(argv[1], 1);
close_io();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment