picchick
A utility to aid in programming PIC microcontrollers
#include <Arduino.h>
#include "picchick.h"
#include "serial_commands.h"
#include "icsp_commands.h"
uint8_t input_buffer[INPUT_BUFFER_SIZE];
size_t recv_size;
bool cmd_is(const char *cmd)
{
if (memcmp(input_buffer, cmd, strlen(cmd)) == 0) {
return true;
}
return false;
}
void cmd_resp(const char *resp)
{
Serial.write(resp);
Serial.write(SERIAL_CMD_SEP);
}
void cmd_resp_error(uint8_t *msg, uint8_t msg_len)
{
Serial.write(SERIAL_CMD_ERROR);
Serial.write(SERIAL_CMD_SEP);
Serial.write(msg, msg_len);
}
uint8_t cmd_hello(void)
{
icsp_init_pins();
cmd_resp(SERIAL_CMD_HELLO);
return STATUS_CONNECTED;
}
uint8_t cmd_bye(void)
{
icsp_reset_pins();
cmd_resp(SERIAL_CMD_BYE);
return STATUS_DISCONNECTED;
}
uint8_t cmd_start(void)
{
icsp_enter_program_mode();
cmd_resp(SERIAL_CMD_OK);
return STATUS_PROGRAM;
}
uint8_t cmd_stop(void)
{
icsp_exit_program_mode();
cmd_resp(SERIAL_CMD_OK);
return STATUS_CONNECTED;
}
uint8_t cmd_addr(void)
{
Serial.readBytes(input_buffer, 2);
uint16_t address = (input_buffer[0] << 8) | input_buffer[1];
icsp_write_command(ICSP_CMD_ADDR_LOAD);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_address(&address);
cmd_resp(SERIAL_CMD_OK);
return STATUS_PROGRAM;
}
uint8_t cmd_word(void)
{
Serial.readBytes(input_buffer, 5);
if (input_buffer[2] != ':') {
cmd_resp_error(input_buffer, 5);
return STATUS_PROGRAM;
}
uint16_t address = (input_buffer[0] << 8) | input_buffer[1];
uint16_t word = (input_buffer[3] << 8) | input_buffer[4];
// Load address
icsp_write_command(ICSP_CMD_ADDR_LOAD);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_address(&address);
delayMicroseconds(ICSP_DELAY_DLY);
// Write word
icsp_write_command(ICSP_CMD_LOAD_DATA);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_payload(&word);
delayMicroseconds(ICSP_DELAY_DLY);
// Begin write command
icsp_write_command(ICSP_CMD_START_INT);
delayMicroseconds(ICSP_DELAY_PINT_CW);
// verify data
// Return response
cmd_resp(SERIAL_CMD_OK);
return STATUS_PROGRAM;
}
uint8_t cmd_row(void)
{
// Get address
recv_size = Serial.readBytesUntil(SERIAL_CMD_SEP, input_buffer, INPUT_BUFFER_SIZE);
if (recv_size != 2) {
cmd_resp_error(input_buffer, recv_size);
return STATUS_PROGRAM;
}
uint16_t address = (input_buffer[0] << 8) | input_buffer[1];
// Get row
recv_size = Serial.readBytes(input_buffer, 128);
if (recv_size != 128) {
cmd_resp_error(input_buffer, recv_size);
return STATUS_PROGRAM;
}
// Load address
icsp_write_command(ICSP_CMD_ADDR_LOAD);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_address(&address);
delayMicroseconds(ICSP_DELAY_DLY);
// Write 63 words of data
uint16_t word;
uint8_t offset;
for (offset=0; offset < 126; offset += 2) {
// Write word
word = (input_buffer[offset] << 8) | input_buffer[offset+1];
icsp_write_command(ICSP_CMD_LOAD_DATA_INC);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_payload(&word);
delayMicroseconds(ICSP_DELAY_DLY);
}
//Write 64th word without incrementing address
word = (input_buffer[offset] << 8) | input_buffer[offset+1];
icsp_write_command(ICSP_CMD_LOAD_DATA);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_payload(&word);
delayMicroseconds(ICSP_DELAY_DLY);
// Begin write command
icsp_write_command(ICSP_CMD_START_INT);
delayMicroseconds(ICSP_DELAY_PINT_PM);
// verify data
// Return response
cmd_resp(SERIAL_CMD_OK);
return STATUS_PROGRAM;
}
uint8_t cmd_erase(void)
{
recv_size = Serial.readBytes(input_buffer, 2);
if (recv_size != 2) {
cmd_resp_error(input_buffer, recv_size);
}
uint16_t address = (input_buffer[0] << 8) | input_buffer[1];
uint8_t cmd = ICSP_CMD_ERASE_ROW;
uint16_t delay_len = ICSP_DELAY_ERAR;
// Bulk erase the whole device
if (address == SERIAL_CMD_ERASE_ALL) {
address = 0x8000;
cmd = ICSP_CMD_ERASE_BULK;
delay_len = ICSP_DELAY_ERAB;
}
// Bulk erase user flash
else if (address == SERIAL_CMD_ERASE_FLASH) {
address = 0x0000;
cmd = ICSP_CMD_ERASE_BULK;
delay_len = ICSP_DELAY_ERAB;
}
icsp_write_command(ICSP_CMD_ADDR_LOAD);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_address(&address);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_command(cmd);
delayMicroseconds(delay_len);
cmd_resp(SERIAL_CMD_OK);
return STATUS_PROGRAM;
}
uint8_t cmd_read(void)
{
recv_size = Serial.readBytes(input_buffer, 2);
if (recv_size != 2) {
cmd_resp_error(input_buffer, recv_size);
}
uint16_t address = (input_buffer[0] << 8) | input_buffer[1];
icsp_write_command(ICSP_CMD_ADDR_LOAD);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_address(&address);
delayMicroseconds(ICSP_DELAY_DLY);
icsp_write_command(ICSP_CMD_READ_DATA);
delayMicroseconds(ICSP_DELAY_DLY);
uint16_t word = icsp_read_word();
// Return response
cmd_resp(SERIAL_CMD_OK);
Serial.write((word >> 8));
Serial.write(word);
return STATUS_PROGRAM;
}
uint8_t handle_command(void)
{
recv_size = Serial.readBytesUntil(':', input_buffer, INPUT_BUFFER_SIZE);
// Greeting
if (cmd_is(SERIAL_CMD_HELLO))
return cmd_hello();
else if (cmd_is(SERIAL_CMD_BYE))
return cmd_bye();
else if (cmd_is(SERIAL_CMD_START))
return cmd_start();
else if (cmd_is(SERIAL_CMD_STOP))
return cmd_stop();
// else if (cmd_is(SERIAL_CMD_ADDR)) This doesnt really need to be a command
// return cmd_addr(); since we specify address in all the others
else if (cmd_is(SERIAL_CMD_WORD))
return cmd_word();
else if (cmd_is(SERIAL_CMD_ROW))
return cmd_row();
else if (cmd_is(SERIAL_CMD_ERASE))
return cmd_erase();
else if (cmd_is(SERIAL_CMD_READ))
return cmd_read();
Serial.write("UNKOWN:");
Serial.write(input_buffer, recv_size);
return 0;
}