Permalink
1946d95 Feb 4, 2012
@kevin-pololu @DavidEGrayson
727 lines (618 sloc) 25.9 KB
/*
OrangutanX2.cpp - Library of functions for controlling the auxiliary
processor on the Orangutan X2.
*/
/*
* Written by Ben Schmidel, Mar 2, 2010.
* Copyright (c) 2010-2012 Pololu Corporation. For more information, see
*
* http://www.pololu.com
* http://forum.pololu.com
* http://www.pololu.com/docs/0J18
*
* You may freely modify and share this code, as long as you keep this
* notice intact (including the three links above). Licensed under the
* Creative Commons BY-SA 3.0 license:
*
* http://creativecommons.org/licenses/by-sa/3.0/
*
* Disclaimer: To the extent permitted by law, Pololu provides this work
* without any warranty. It might be defective, in which case you agree
* to be responsible for all resulting costs and damages.
*/
#include "../OrangutanResources/include/OrangutanModel.h"
#include "../OrangutanSPIMaster/OrangutanSPIMaster.h"
#include "../OrangutanTime/OrangutanTime.h"
#include "OrangutanX2.h"
#ifdef _ORANGUTAN_X2
extern "C" void x2_get_firmware_version(unsigned char *vmajor, unsigned char *vminor)
{
OrangutanX2::getFirmwareVersion(*vmajor, *vminor);
}
extern "C" unsigned char x2_get_status()
{
return OrangutanX2::getStatus();
}
extern "C" void x2_restore_default_settings()
{
OrangutanX2::restoreDefaultSettings();
}
extern "C" void x2_save_avrisp_version(unsigned char vmajor, unsigned char vminor)
{
OrangutanX2::saveAVRISPVersion(vmajor, vminor);
}
extern "C" void x2_save_eeprom_byte(unsigned int address, unsigned char data)
{
OrangutanX2::saveEEPROMByte(address, data);
}
extern "C" void x2_save_parameter(unsigned int param_address, unsigned char param_value)
{
OrangutanX2::saveParameter(param_address, param_value);
}
extern "C" unsigned char x2_read_eeprom_byte(unsigned int address)
{
return OrangutanX2::readEEPROMByte(address);
}
extern "C" unsigned char x2_read_parameter(unsigned int param_address)
{
return OrangutanX2::readParameter(param_address);
}
// ***************** MOTORS *****************
extern "C" void x2_set_motor(unsigned char motor, unsigned char operation_mode, int speed)
{
OrangutanX2::setMotor(motor, operation_mode, speed);
}
extern "C" void x2_set_pwm_frequencies(unsigned char m1_resolution, unsigned char m1_prescaler,
unsigned char m2_resolution, unsigned char m2_prescaler, unsigned char save)
{
OrangutanX2::setPWMFrequencies(m1_resolution, m1_prescaler, m2_resolution, m2_prescaler, save);
}
extern "C" void x2_set_acceleration(unsigned char motor, unsigned char accel, unsigned char save)
{
OrangutanX2::setAcceleration(motor, accel, save);
}
extern "C" void x2_set_brake_duration(unsigned char motor, unsigned char brake_dur, unsigned char save)
{
OrangutanX2::setBrakeDuration(motor, brake_dur, save);
}
extern "C" void x2_set_num_current_samples(unsigned char m1_exp, unsigned char m2_exp, unsigned char save)
{
OrangutanX2::setNumCurrentSamples(m1_exp, m2_exp, save);
}
extern "C" void x2_set_current_limit(unsigned char motor, unsigned char limit, unsigned char p,
unsigned char save)
{
OrangutanX2::setCurrentLimit(motor, limit, p, save);
}
extern "C" unsigned char x2_get_motor_current(unsigned char motor)
{
return OrangutanX2::getMotorCurrent(motor);
}
// ***************** BUZZER *****************
extern "C" void x2_play_note(unsigned char note, unsigned int duration)
{
OrangutanX2::playNote(note, duration);
}
extern "C" void x2_play_frequency(unsigned int frequency, unsigned int duration)
{
OrangutanX2::playFrequency(frequency, duration);
}
extern "C" void x2_buzzer_off()
{
OrangutanX2::buzzerOff();
}
extern "C" void x2_set_volume(unsigned char volume, unsigned char save)
{
OrangutanX2::setVolume(volume, save);
}
extern "C" void x2_set_note_gap(unsigned char note_gap, unsigned char save)
{
OrangutanX2::setNoteGap(note_gap, save);
}
// ***************** UART (SERIAL) *****************
extern "C" void x2_set_serial(unsigned char parity, unsigned char stop_bits, unsigned char speed_mode,
unsigned int baud_ubrr, unsigned char save)
{
OrangutanX2::setSerial(parity, stop_bits, speed_mode, baud_ubrr, save);
}
extern "C" void x2_enable_permanent_programming_mode(unsigned char save)
{
OrangutanX2::enablePermanentProgMode(save);
}
extern "C" void x2_set_read_ready_size(unsigned char rrsize, unsigned char save)
{
OrangutanX2::setReadReadySize(rrsize, save);
}
extern "C" unsigned char x2_get_tx_buffer_space()
{
return OrangutanX2::getTXBufferSpace();
}
extern "C" unsigned char x2_get_num_rx_bytes()
{
return OrangutanX2::getNumRXBytes();
}
extern "C" unsigned char x2_get_serial_error()
{
return OrangutanX2::getSerialError();
}
extern "C" unsigned char x2_send_serial_byte_if_ready(unsigned char data)
{
return OrangutanX2::sendSerialByteIfReady(data);
}
extern "C" unsigned char x2_read_serial_byte()
{
return OrangutanX2::readSerialByte();
}
// After this method is called, the two arguments will hold the values of the major firmware
// version byte and minor firmware version byte. The firmware version is
// majorVersion.minorVersion
void OrangutanX2::getFirmwareVersion(unsigned char &majorVersion, unsigned char &minorVersion)
{
OrangutanSPIMaster::transmitAndDelay(CMD_GET_FIRMWARE_VERSION, 3);
majorVersion = OrangutanSPIMaster::transmitAndDelay(0, 3);
minorVersion = OrangutanSPIMaster::transmit(0);
}
// Most bits of the status byte are latched once set. Calling this function returns the status
// byte and clears any latched status bits. The status bits are enumerated as follows:
// bit 0 = UART error (*LATCHED* when set, cleared when read)
// bit 1 = UART read ready (value always reflects current state)
// bit 2 = UART send buffer full (value always reflects current state)
// bit 3 = buzzer finished (value always reflects current state)
// bit 4 = motor 1 fault (*LATCHED* when set, cleared when read)
// bit 5 = motor 1 overcurrent (*LATCHED* when set, cleared when read)
// bit 6 = motor 2 fault (*LATCHED* when set, cleared when read)
// bit 7 = motor 2 overcurrent (*LATCHED* when set, cleared when read)
unsigned char OrangutanX2::getStatus()
{
OrangutanSPIMaster::transmitAndDelay(CMD_GET_STATUS, 3);
return OrangutanSPIMaster::transmit(0);
}
// At the time of the X2's release, the current AVR Studio AVRISP version is
// 2.0A. The arguments to this function represent the version AVRISP version
// number the mega168 will transmit to AVR Studio when you attempt to program
// the mega644. If the two version don't match, you will have to first cancel
// out of an "upgrade your firmware?" dialog box every time you try to program
// your mega644 (you cannot upgrade your firmware, anyway, so this dialog is
// nothing more than an annoyance). As such, you might have a better
// programming experience if you set these version values to match the AVRISP
// version used by the AVR Studio you're running (if you use AVR Studio).
void OrangutanX2::saveAVRISPVersion(unsigned char vMajor, unsigned char vMinor)
{
// version is "vMajor.vMinor"
writeToEEPROM(ADDR_ISP_SW_MAJOR, vMajor);
writeToEEPROM(ADDR_ISP_SW_MINOR, vMinor);
}
// Write a byte to the specified mega168's EEPROM address. This command
// can be used to store settings so that they persist after a hardware reset.
// Settings occupy EEPROM bytes 0 - 23 and melody pointers occupy bytes
// 24 - 32. Melody notes are saved to EEPROM in three-byte blocks starting
// at address 33.
// This method is PRIVATE and should be accessed using the public methods
// saveEEPROMByte() and saveParameter()
void OrangutanX2::writeToEEPROM(unsigned int address, unsigned char data)
{
if (address >= 512) // address out of bounds
return;
waitForEEPROM(); // wait for any current EEPROM writes to finish
// insert data MSB and address bits 7 and 8 into the command byte
OrangutanSPIMaster::transmit(CMD_WRITE_EEPROM | ((data & 0x80) >> 5)
| ((address & 0x0080) >> 6)
| ((address & 0x0100) >> 8));
OrangutanSPIMaster::transmit((unsigned char)(address & 0x007F));
OrangutanSPIMaster::transmit(data & 0x7F);
}
// Read a byte from the mega168's EEPROM. This command can be used to check
// the values of the settings that are loaded when the 168 is reset.
// Settings occupy EEPROM bytes 0 - 23.
unsigned char OrangutanX2::readEEPROMByte(unsigned int address)
{
if (address >= 512) // address out of bounds
return 0;
waitForEEPROM(); // wait for any current EEPROM writes to finish
// insert address bits 7 and 8 into the command byte
OrangutanSPIMaster::transmit(CMD_READ_EEPROM | ((address & 0x0080) >> 6)
| ((address & 0x0100) >> 8));
OrangutanSPIMaster::transmitAndDelay((unsigned char)(address & 0x007F), 3);
return OrangutanSPIMaster::transmit(0); // send a junk data byte here
}
// Check to see if the mega168's EEPROM is currently being written to, which
// means it is not possible to read from it or start a new write. This is
// a PRIVATE method.
unsigned char OrangutanX2::isEEPROMBusy()
{
OrangutanSPIMaster::transmitAndDelay(CMD_GET_EEPROM_BUSY, 3);
return OrangutanSPIMaster::transmit(0); // send a junk data byte here
}
//****************************************************************************
// Motor
//****************************************************************************
// motor: 0 for M1, 1 for M2, 0xFF for joint
// operationMode: 0 for immediate drive, 1 for acceleration drive, 0xFF for brake low
// speed: -255 to 255 (absolute value of speed used when braking, otherwise sign = direction)
void OrangutanX2::setMotor(unsigned char motor, unsigned char operationMode, int speed)
{
static unsigned char inJointMotorMode = 0;
if (motor == JOINT_MOTOR && !inJointMotorMode)
{
OrangutanSPIMaster::transmit(CMD_ENABLE_JOINT_MOTOR_MODE); // enable joint-motor mode
inJointMotorMode = 1;
}
if (motor != JOINT_MOTOR && inJointMotorMode)
{
OrangutanSPIMaster::transmit(CMD_DISABLE_JOINT_MOTOR_MODE); // disable joint-motor mode
inJointMotorMode = 0;
}
unsigned char cmd;
if (motor == JOINT_MOTOR) // if joint motor command
{
if (operationMode == ACCEL_DRIVE) // if accel
cmd = CMD_JOINT_ACCEL_FORWARD; // joint motor accel forward
else if (operationMode == BRAKE_LOW) // if brake
cmd = CMD_JOINT_BRAKE; // joint motor brake low
else // else immediate drive
cmd = CMD_JOINT_FORWARD; // joint motor forward
}
else
{
if (motor != MOTOR2)
motor = MOTOR1;
if (operationMode == ACCEL_DRIVE) // if accel
cmd = CMD_MOTOR1_ACCEL_FORWARD + motor * 4; // motor accel forward
else if (operationMode == BRAKE_LOW) // if brake
cmd = CMD_MOTOR1_BRAKE_LOW + motor * 4; // motor brake low
else // else immediate drive
cmd = CMD_MOTOR1_FORWARD + motor * 4; // motor forward
}
if (speed < 0)
{
speed = -speed; // make speed a positive quantity
if (operationMode != BRAKE_LOW) // if not braking
cmd += 2; // turn command from forward to reverse
}
if (speed > 255)
speed = 255;
// set LSB of command byte if speed has MSB set (MSB of speed is sent as LSB of cmd)
OrangutanSPIMaster::transmit(((unsigned char)speed) & 0x80 ? cmd | 1 : cmd);
// data byte is 7 low bits of speed
OrangutanSPIMaster::transmit(((unsigned char)speed) & 0x7F);
}
// PWM frequency is determined by the resolution (either 7-bit or 8-bit) and
// prescaler used to convert the 20MHz IO clk into the PWM clock. The formula
// is frequency = 20MHz / prescaler / 2^(bit-resolution).
// For example: 7-bit resolution with a prescaler of 8 gives PWM freq = 19.5 kHz.
// Note that VHN2SP30 motor drivers can handle PWMs up to 20 kHz, but VNH3SP30
// motor drivers can only handle PWMs up to 10 kHz, so you should not use
// 7-bit resolution with a prescaler of 8 if your Orangutan X2 has VNH3SP30
// motor drivers.
// Arguments passed to this function should be the RESOLUTION_ and PRESCALER_
// #defines in OrangutanX2.h.
// Default resolution is 8-bit and default presaler is 8, so default PWM frequency
// is 9.8 kHz.
void OrangutanX2::setPWMFrequencies(unsigned char M1Resolution, unsigned char M1Prescaler,
unsigned char M2Resolution, unsigned char M2Prescaler, unsigned char save)
{
if ( M1Resolution != RESOLUTION_7BIT )
M1Resolution = RESOLUTION_8BIT;
if ( M2Resolution != RESOLUTION_7BIT )
M2Resolution = RESOLUTION_8BIT;
if ( M1Prescaler > PRESCALER_1024 )
M1Prescaler = PRESCALER_1024;
if ( M2Prescaler > PRESCALER_1024 )
M2Prescaler = PRESCALER_1024;
OrangutanSPIMaster::transmit(CMD_SET_PWM_FREQUENCIES);
OrangutanSPIMaster::transmit((M2Resolution << 5) | (M2Prescaler << 3) |
(M1Resolution << 2) | M1Prescaler);
if (save)
{
writeToEEPROM(ADDR_M1_PWM_FREQUENCY, (M1Resolution << 2) | M1Prescaler);
writeToEEPROM(ADDR_M2_PWM_FREQUENCY, (M2Resolution << 2) | M2Prescaler);
}
}
// Sets the accelerations used by the accelMotor functions. Acceleration commands
// cause the motor's pwm to be incremented by accel/10 every 10ms. Acceleration
// only applies when the PWM duty cycle is increasing or when the motor changes
// direction. Decreases in PWM duty cycle occur immediately (i.e. there is no
// deceleration). The net effect is that the pwm increases by accel every 100ms
// when the motor is commanded to increase speed. When the motor is commanded
// to change direction, the PWM duty cycle is immediately set to zero, a brake
// duration occurs if the brake duration parameter is nonzero, and then the
// PWM duty cycle increases from zero to the target speed at a rate of accel
// per 100 ms.
// An accel of zero = "infinite" acceleration. Accel must be 127 or less.
// Default acceleration is 25 (i.e. 1 second to go from speed 0 to 250).
void OrangutanX2::setAcceleration(unsigned char motor, unsigned char accel,
unsigned char save)
{
if (accel > 127)
accel= 127;
unsigned char cmd = CMD_SET_M1_ACCELERATION;
unsigned char param = ADDR_M1_ACCELERATION;
if (motor == MOTOR2)
{
cmd = CMD_SET_M2_ACCELERATION;
param = ADDR_M2_ACCELERATION;
}
OrangutanSPIMaster::transmit(cmd);
OrangutanSPIMaster::transmit(accel);
if (save)
writeToEEPROM(param, accel);
}
// Sets the amount of time the motor will brake low at 100% duty cycle when
// it receives an acceleration command that will result in a change of
// direction. The units of brakeDuration are 10ms, which means an argument
// value of 1 will result in a brake duration of 10ms and an argument value
// of 127 will result in a brake duration of 1.27s. If brakeDuration is zero,
// there is no braking before an acceleration direction change. brakeDuration
// must be 127 or less. These functions will only work with X2 firmware
// versions 1.01 or later.
// Default brake duration is 10 (i.e. brake for 100ms on direction change).
void OrangutanX2::setBrakeDuration(unsigned char motor, unsigned char brakeDur,
unsigned char save)
{
if (brakeDur > 127)
brakeDur = 127;
unsigned char cmd = CMD_SET_M1_BRAKE_DURATION;
unsigned char param = ADDR_M1_BRAKE_DURATION;
if (motor == MOTOR2)
{
cmd = CMD_SET_M2_BRAKE_DURATION;
param = ADDR_M2_BRAKE_DURATION;
}
OrangutanSPIMaster::transmit(cmd);
OrangutanSPIMaster::transmit(brakeDur);
if (save)
writeToEEPROM(param, brakeDur);
}
// The number of current samples in the running current averages is given by:
// M1 average current averages the last 2 ^ M1Exponent samples
// M2 average current averages the last 2 ^ M2Exponent samples
// The exponents may range from 0 - 7 (which equates to 1, 2, 4, 8, 16, 32,
// 64, or 128 samples). More samples means less noise, but it also means
// older data is affecting your present measurement.
// Note: measuring current is only possible on Orangutan X2s with VNH2SP30
// motor drivers. The VNH3SP30 does not support current sense feedback.
// Default number of averaged current samples is 128 (i.e. default exponent is 7).
void OrangutanX2::setNumCurrentSamples( unsigned char M1Exponent, unsigned char M2Exponent,
unsigned char save)
{
if ( M1Exponent > 7 )
M1Exponent = 7;
if ( M2Exponent > 7 )
M2Exponent = 7;
OrangutanSPIMaster::transmit(CMD_SET_NUM_CURRENT_SAMPLES);
OrangutanSPIMaster::transmit((M2Exponent << 3) | M1Exponent);
if (save)
{
writeToEEPROM(ADDR_M1_CURRENT_SAMPLES, 1 << M1Exponent);
writeToEEPROM(ADDR_M2_CURRENT_SAMPLES, 1 << M2Exponent);
}
}
// If current limit is zero, there is no limit. Otherwise, if the current
// average ever exceeds the limit, P determines what happens. If P is zero,
// the motor shuts off. If P is non-zero, the motor's PWM is decreased by
// P * (current - limit). The PWM will never increase by an amount greater
// than P * | limit - current | while accelerating, either. There are
// no restrictions on limit; P must be no greater than 127. These functions
// will only work with X2 firmware version 1.01 or later.
// Note: current limiting is only possible on Orangutan X2s with VNH2SP30
// motor drivers. The VNH3SP30 does not support current sense feedback.
// Default current limit is 0 (i.e. no current limit) and default
// proportionality constant P is 5.
void OrangutanX2::setCurrentLimit(unsigned char motor, unsigned char limit,
unsigned char P, unsigned char save)
{
if (P > 127)
P = 127;
unsigned char cmd = CMD_SET_M1_CURRENT_LIMIT;
unsigned char param1 = ADDR_M1_CURRENT_LIMIT;
unsigned char param2 = ADDR_M1_CL_P_CONST;
if (motor == MOTOR2)
{
cmd = CMD_SET_M2_CURRENT_LIMIT;
param1 = ADDR_M2_CURRENT_LIMIT;
param2 = ADDR_M2_CL_P_CONST;
}
OrangutanSPIMaster::transmit(limit & 0x80 ? cmd | 1 : cmd);
OrangutanSPIMaster::transmit(limit & 0x7F);
OrangutanSPIMaster::transmit(P);
if (save)
{
writeToEEPROM(param1, limit);
writeToEEPROM(param2, P);
}
}
// Get the running current average for the specified motor
// Note: measuring current is only possible on Orangutan X2s with VNH2SP30
// motor drivers. The VNH3SP30 does not support current sense feedback.
unsigned char OrangutanX2::getMotorCurrent(unsigned char motor)
{
unsigned char cmd = CMD_GET_M1_CURRENT;
if (motor == MOTOR2)
cmd = CMD_GET_M2_CURRENT;
OrangutanSPIMaster::transmitAndDelay(cmd, 3);
return OrangutanSPIMaster::transmit(0);
}
//****************************************************************************
// Buzzer
//****************************************************************************
// Silence the buzzer immediately
void OrangutanX2::buzzerOff()
{
OrangutanSPIMaster::transmit(CMD_BUZZER_OFF);
}
// Play the specified note for the specified duration (in ms) immediately.
// Note enumeration is provided as a set of #define macros in OrangutanBuzzer.h.
void OrangutanX2::playNote(unsigned char note, unsigned int duration)
{
unsigned char *byte_ptr = (unsigned char*)&duration;
unsigned char lo = *byte_ptr; // low byte of duration
unsigned char hi = *(byte_ptr + 1); // high byte of duration
unsigned char cmd = CMD_PLAY_NOTE;
if (lo & 0x80)
cmd |= 4;
if (hi & 0x80)
cmd |= 2;
if (note & 0x80)
cmd |= 1;
// insert the MSBs of the three data bytes into the command byte
OrangutanSPIMaster::transmit(cmd);
OrangutanSPIMaster::transmit(note & 0x7F);
OrangutanSPIMaster::transmit(hi & 0x7F);
OrangutanSPIMaster::transmit(lo & 0x7F);
}
// Play the specified frequency (in Hz) for the specified duration (in ms)
// immediately. Frequency must be no greater than 0x7FFF (15-bit value).
// The mega168 can only play frequencies as low as 40Hz and as high as
// 10kHz. Values outside this range will just produce the nearest
// allowed frequency.
void OrangutanX2::playFrequency(unsigned int frequency, unsigned int duration)
{
if (frequency > 0x7FFF)
frequency = 0x7FFF; // frequency must be a 15-bit value
unsigned char *byte_ptr = (unsigned char*)&frequency;
unsigned char loFreq = *byte_ptr;
unsigned char hiFreq = *(byte_ptr + 1);
byte_ptr = (unsigned char*)&duration;
unsigned char loDur = *byte_ptr;
unsigned char hiDur = *(byte_ptr + 1);
unsigned char cmd = CMD_PLAY_FREQUENCY;
if (loDur & 0x80)
cmd |= 4;
if (hiDur & 0x80)
cmd |= 2;
if (loFreq & 0x80)
cmd |= 1;
// insert the MSBs of the two duration bytes and the low frequency byte
// MSB of the high frequency byte is guaranteed to be zero
OrangutanSPIMaster::transmit(cmd);
OrangutanSPIMaster::transmit(hiFreq);
OrangutanSPIMaster::transmit(loFreq & 0x7F);
OrangutanSPIMaster::transmit(hiDur & 0x7F);
OrangutanSPIMaster::transmit(loDur & 0x7F);
}
// Sets the volume of the buzzer by changing the duty cycle of the pwm driving
// it. buzzer duty cycle = 1 >> ( 16 - volume ). volume must range from
// 0 - 15. Default volume is 12.
void OrangutanX2::setVolume(unsigned char volume, unsigned char save)
{
if (volume > 15)
volume = 15; // must be a 4-bit value
OrangutanSPIMaster::transmit(CMD_SET_VOLUME);
OrangutanSPIMaster::transmit(volume);
if (save)
writeToEEPROM(ADDR_BUZZER_VOLUME, volume);
}
// noteGap determines the silent pause (in ms) that's inserted after every note
// the buzzer plays. noteGap must be no greater than 127.
// Default note gap is 5 (5ms pause between notes).
void OrangutanX2::setNoteGap(unsigned char noteGap, unsigned char save)
{
if (noteGap > 127)
noteGap = 127;
OrangutanSPIMaster::transmit(CMD_SET_NOTE_GAP);
OrangutanSPIMaster::transmit(noteGap);
if (save)
writeToEEPROM(ADDR_NOTE_GAP, noteGap);
}
//****************************************************************************
// UART
//****************************************************************************
// Sets the UART parameters. Arguments to this function should be the
// UART #defines in OrangutahX2.h. speedMode is either UART_NORMAL_SPEED or
// UART_DOUBLE_SPEED. This command will disable permanent progmode
// if it's enabled. If the argument "save" is nonzero, the serial parameters
// are also saved to EEPROM and will persist after a power cycle.
// Default settings are: normal UART functionality, no parity, one stop bit,
// normal speed (i.e. not double-speed mode), 115.2kbps baud rate. The
// UART character size is always 8-bits.
void OrangutanX2::setSerial(unsigned char parity, unsigned char stopBits,
unsigned char speedMode, unsigned int baud_UBRR, unsigned char save)
{
// ensure parameter values are all valid
if ((parity != UART_EVEN_PARITY) && (parity != UART_ODD_PARITY))
parity = UART_NO_PARITY;
if (stopBits != UART_ONE_STOP_BIT)
stopBits = UART_TWO_STOP_BITS;
if (speedMode != UART_NORMAL_SPEED)
speedMode = UART_DOUBLE_SPEED;
if (baud_UBRR > 2047) // UBRR must be an 11-bit value
baud_UBRR = 2047;
unsigned char cmd = CMD_SET_SERIAL;
if (save)
{
cmd = CMD_SET_AND_SAVE_SERIAL;
waitForEEPROM();
}
OrangutanSPIMaster::transmit(cmd | (parity << 1));
OrangutanSPIMaster::transmit((stopBits << 5) | (speedMode << 4) |
((baud_UBRR & 0x0780) >> 7)); // send the four MSBs of UBRR
OrangutanSPIMaster::transmit(baud_UBRR & 0x007F); // send the seven LSBs of UBBR
}
// This command reserves the UART solely for programming the mega644. In
// permanent program mode you do not have to explicitly put the X2 into
// programming mode to program the mega644. This command overrides all
// current UART settings. This setting can be made to persist after a
// hardware reset by saving the value 0x40 to mega168 EEPROM address 13.
void OrangutanX2::enablePermanentProgMode(unsigned char save)
{
OrangutanSPIMaster::transmit(CMD_SET_SERIAL);
OrangutanSPIMaster::transmit(0x40);
OrangutanSPIMaster::transmit(0);
if (save)
writeToEEPROM(ADDR_SERIAL_SETTINGS, 0x40);
}
// Determines how many bytes must be in the UART read buffer before the status
// byte and attention line indicate that it is ready to be read.
// Default value is 1 (i.e. read-ready flag set after every received byte).
void OrangutanX2::setReadReadySize(unsigned char rrSize, unsigned char save)
{
// rrSize must be in the range of 1 - UART_READ_BUFF_SZ (32)
if (rrSize == 0)
rrSize = 1;
if (rrSize > UART_READ_BUFF_SZ)
rrSize = UART_READ_BUFF_SZ;
OrangutanSPIMaster::transmit(CMD_SET_READ_READY_SIZE);
OrangutanSPIMaster::transmit(rrSize - 1); // transmit one less than desired value
if (save)
writeToEEPROM(ADDR_SERIAL_READ_READY, rrSize);
}
// Get number of unused bytes in the UART send buffer
unsigned char OrangutanX2::getTXBufferSpace()
{
OrangutanSPIMaster::transmitAndDelay(CMD_GET_SEND_BUFF_FREE_SPACE, 3);
return OrangutanSPIMaster::transmit(0); // junk data byte
}
// Get number of unread bytes in the UART read buffer
unsigned char OrangutanX2::getNumRXBytes()
{
OrangutanSPIMaster::transmitAndDelay(CMD_GET_READ_BUFF_NUM_BYTES, 3);
return OrangutanSPIMaster::transmit(0); // junk data byte
}
// When a serial error occurs, the appropriate bit in the UART error byte is set and
// latched, and the STATUS_UART_ERROR bit of the status byte is set and the X2's
// ATT line is set. Calling this function returns the UART error byte and then
// clears it. The error byte bits are enumerated in OrangutanX2.h.
unsigned char OrangutanX2::getSerialError()
{
OrangutanSPIMaster::transmitAndDelay(CMD_GET_UART_ERROR, 3);
return OrangutanSPIMaster::transmit(0);
}
// Send a byte to the mega168 to be queued for transmission over the UART.
// While queued the byte will reside in the mega168's UART send buffer.
// If there is no room in the transmit buffer for the byte to be sent, this method
// returns 0, otherwise it returns 1.
unsigned char OrangutanX2::sendSerialByteIfReady(unsigned char data)
{
if (getTXBufferSpace() == 0)
return 0;
OrangutanSPIMaster::transmit(data & 0x80 ? CMD_SEND_SERIAL | 1 : CMD_SEND_SERIAL);
OrangutanSPIMaster::transmit(data & 0x7F);
return 1;
}
// Retrieve the next byte from the mega168's UART read buffer.
unsigned char OrangutanX2::readSerialByte()
{
OrangutanSPIMaster::transmitAndDelay(CMD_READ_SERIAL, 3);
return OrangutanSPIMaster::transmit(0); // junk data byte
}
#endif