No changes between revisions
/Modules/HBRIDGE/HB2FET02A/SW/avr4.x
0,0 → 1,168
/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
OUTPUT_ARCH(avr:4)
MEMORY
{
text (rx) : ORIGIN = 0, LENGTH = 8K
data (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
}
SECTIONS
{
/* Read-only sections, merged into text segment: */
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text :
{
*(.rel.text)
*(.rel.text.*)
*(.rel.gnu.linkonce.t*)
}
.rela.text :
{
*(.rela.text)
*(.rela.text.*)
*(.rela.gnu.linkonce.t*)
}
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata :
{
*(.rel.rodata)
*(.rel.rodata.*)
*(.rel.gnu.linkonce.r*)
}
.rela.rodata :
{
*(.rela.rodata)
*(.rela.rodata.*)
*(.rela.gnu.linkonce.r*)
}
.rel.data :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.gnu.linkonce.d*)
}
.rela.data :
{
*(.rela.data)
*(.rela.data.*)
*(.rela.gnu.linkonce.d*)
}
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss) }
.rela.bss : { *(.rela.bss) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
/* Internal text space or external memory */
.text :
{
KEEP (*(.vectors))
__ctors_start = . ;
KEEP (*(.ctors))
__ctors_end = . ;
__dtors_start = . ;
KEEP (*(.dtors))
__dtors_end = . ;
*(.progmem.gcc*)
*(.progmem*)
. = ALIGN(2);
KEEP (*(.init0)) /* Start here after reset. */
KEEP (*(.init1))
KEEP (*(.init2)) /* Clear __zero_reg__, set up stack pointer. */
KEEP (*(.init3))
KEEP (*(.init4)) /* Initialize data and BSS. */
KEEP (*(.init5))
KEEP (*(.init6)) /* C++ constructors. */
KEEP (*(.init7))
KEEP (*(.init8))
KEEP (*(.init9)) /* Call main(). */
*(.text)
. = ALIGN(2);
*(.text.*)
. = ALIGN(2);
*(.fini9) /* _exit() starts here. */
*(.fini8)
*(.fini7)
*(.fini6) /* C++ destructors. */
*(.fini5)
*(.fini4)
*(.fini3)
*(.fini2)
*(.fini1)
*(.fini0) /* Infinite loop after program termination. */
_etext = . ;
} > text
.data : AT (ADDR (.text) + SIZEOF (.text))
{
PROVIDE (__data_start = .) ;
*(.data)
*(.gnu.linkonce.d*)
. = ALIGN(2);
_edata = . ;
PROVIDE (__data_end = .) ;
} > data
.bss SIZEOF(.data) + ADDR(.data) :
{
PROVIDE (__bss_start = .) ;
*(.bss)
*(COMMON)
PROVIDE (__bss_end = .) ;
} > data
__data_load_start = LOADADDR(.data);
__data_load_end = __data_load_start + SIZEOF(.data);
/* Global data not cleared after reset. */
.noinit SIZEOF(.bss) + ADDR(.bss) :
{
PROVIDE (__noinit_start = .) ;
*(.noinit*)
PROVIDE (__noinit_end = .) ;
_end = . ;
PROVIDE (__heap_start = .) ;
} > data
.eeprom :
{
*(.eeprom*)
__eeprom_end = . ;
} > eeprom
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
}
/Modules/HBRIDGE/HB2FET02A/SW/avr5.x
0,0 → 1,168
/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
OUTPUT_ARCH(avr:5)
MEMORY
{
text (rx) : ORIGIN = 0, LENGTH = 128K
data (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
}
SECTIONS
{
/* Read-only sections, merged into text segment: */
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text :
{
*(.rel.text)
*(.rel.text.*)
*(.rel.gnu.linkonce.t*)
}
.rela.text :
{
*(.rela.text)
*(.rela.text.*)
*(.rela.gnu.linkonce.t*)
}
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata :
{
*(.rel.rodata)
*(.rel.rodata.*)
*(.rel.gnu.linkonce.r*)
}
.rela.rodata :
{
*(.rela.rodata)
*(.rela.rodata.*)
*(.rela.gnu.linkonce.r*)
}
.rel.data :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.gnu.linkonce.d*)
}
.rela.data :
{
*(.rela.data)
*(.rela.data.*)
*(.rela.gnu.linkonce.d*)
}
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss) }
.rela.bss : { *(.rela.bss) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
/* Internal text space or external memory */
.text :
{
KEEP (*(.vectors))
__ctors_start = . ;
KEEP (*(.ctors))
__ctors_end = . ;
__dtors_start = . ;
KEEP (*(.dtors))
__dtors_end = . ;
*(.progmem.gcc*)
*(.progmem*)
. = ALIGN(2);
KEEP (*(.init0)) /* Start here after reset. */
KEEP (*(.init1))
KEEP (*(.init2)) /* Clear __zero_reg__, set up stack pointer. */
KEEP (*(.init3))
KEEP (*(.init4)) /* Initialize data and BSS. */
KEEP (*(.init5))
KEEP (*(.init6)) /* C++ constructors. */
KEEP (*(.init7))
KEEP (*(.init8))
KEEP (*(.init9)) /* Call main(). */
*(.text)
. = ALIGN(2);
*(.text.*)
. = ALIGN(2);
*(.fini9) /* _exit() starts here. */
*(.fini8)
*(.fini7)
*(.fini6) /* C++ destructors. */
*(.fini5)
*(.fini4)
*(.fini3)
*(.fini2)
*(.fini1)
*(.fini0) /* Infinite loop after program termination. */
_etext = . ;
} > text
.data : AT (ADDR (.text) + SIZEOF (.text))
{
PROVIDE (__data_start = .) ;
*(.data)
*(.gnu.linkonce.d*)
. = ALIGN(2);
_edata = . ;
PROVIDE (__data_end = .) ;
} > data
.bss SIZEOF(.data) + ADDR(.data) :
{
PROVIDE (__bss_start = .) ;
*(.bss)
*(COMMON)
PROVIDE (__bss_end = .) ;
} > data
__data_load_start = LOADADDR(.data);
__data_load_end = __data_load_start + SIZEOF(.data);
/* Global data not cleared after reset. */
.noinit SIZEOF(.bss) + ADDR(.bss) :
{
PROVIDE (__noinit_start = .) ;
*(.noinit*)
PROVIDE (__noinit_end = .) ;
_end = . ;
PROVIDE (__heap_start = .) ;
} > data
.eeprom :
{
*(.eeprom*)
__eeprom_end = . ;
} > eeprom
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
}
/Modules/HBRIDGE/HB2FET02A/SW/common.h
0,0 → 1,33
#ifndef __common_h__
#define __common_h__
 
#define CLRBIT(aReg,aBit) (aReg) &= ~(aBit)
#define SETBIT(aReg,aBit) (aReg) |= (aBit)
#define TOGGLEBIT(aReg,aBit) (aReg) ^= (aBit)
//#define TESTBIT(aValue,aConst) ((aValue & aConst) != 0)
#define TESTBIT(aValue,aConst) ({ \
uint8_t Temp = (uint8_t)(aValue); \
__asm__ ( \
"andi %0, %2" "\n\t" \
"breq L_%=" "\n\t" \
"ldi %0,1" "\n\t" \
"L_%=: " "\n\t" \
: "=r" (Temp) : "0" (Temp), "M" (aConst)\
); \
Temp; \
})
 
 
#if defined BOARD_rs232if
#define BOARD
#elif defined BOARD_umServo32
#define BOARD
#elif defined BOARD_umHBridge
#define BOARD
#elif defined BOARD_NA
#define BOARD
#else
#error BOARD_xxx must be defined
#endif
 
#endif // __common_h__
/Modules/HBRIDGE/HB2FET02A/SW/eeprom.cpp
0,0 → 1,43
#include <avr/pgmspace.h>
#include "eeprom.h"
#include "common.h"
 
void EEPROM::Wait() {
while(TESTBIT(EECR,(1<<EEPE)));
}
 
uint8_t EEPROM::GetByte(uint16_t aOfs) {
// Wait for any pending writes to finish
while(TESTBIT(EECR,(1<<EEPE)));
EEARL = aOfs & 0x00ff;
#ifdef EEARH
EEARH = 0;
#endif
SETBIT(EECR,(1 << EERE));
uint8_t RetVal = EEDR;
return RetVal;
}
 
void EEPROM::SetByte(uint16_t aOfs,uint8_t aData) {
// Wait for any pending writes to finish
while(TESTBIT(EECR,(1<<EEPE)));
EEARL = aOfs & 0x00ff;
#ifdef EEARH
EEARH = 0;
#endif
EEDR = aData;
SETBIT(EECR,(1<<EEMPE));
SETBIT(EECR,(1<<EEPE));
}
 
uint16_t EEPROM::GetWord(uint16_t aOfs) {
uint16_t RetVal;
((uint8_t *)(&RetVal))[0] = GetByte(aOfs+0);
((uint8_t *)(&RetVal))[1] = GetByte(aOfs+1);
return RetVal;
}
 
void EEPROM::SetWord(uint16_t aOfs,uint16_t aData) {
SetByte(aOfs+0,((uint8_t *)(&aData))[0]);
SetByte(aOfs+1,((uint8_t *)(&aData))[1]);
}
/Modules/HBRIDGE/HB2FET02A/SW/eeprom.h
0,0 → 1,18
#ifndef __EEPROM_H__
#define __EEPROM_H__
 
namespace EEPROM {
inline void Init() {
EECR = 0; // Make sure we're erasing and writing at the same time
}
uint8_t GetByte(uint16_t aOfs);
void SetByte(uint16_t aOfs,uint8_t aData);
uint16_t GetWord(uint16_t aOfs);
void SetWord(uint16_t aOfs,uint16_t aData);
void Wait();
}
 
namespace EEPROM_layout {
}
 
#endif // __EEPROM_H__
/Modules/HBRIDGE/HB2FET02A/SW/gentwiuid.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/Modules/HBRIDGE/HB2FET02A/SW/guids.h
0,0 → 1,63
#ifndef __guids_h__
#define __guids_h__
 
#ifdef BOARD
// We're included for the target board. Define various structures
struct GUID {
uint32_t Part1;
uint16_t Part2;
uint16_t Part3;
uint8_t Part4[8];
};
#endif
 
// These are the only valid values for UniqueID items
const uint8_t num0 = 0x00;
const uint8_t num1 = 0x01;
const uint8_t num2 = 0x03;
const uint8_t num3 = 0x07;
const uint8_t num4 = 0x0f;
const uint8_t num5 = 0x1f;
const uint8_t num6 = 0x3f;
const uint8_t num7 = 0x7f;
const uint8_t num8 = 0xff; // this value is never used in any valid uniqueID, however it IS used in the unassigned ID
 
// Unique IDs
#define UniqueIDUnassigned { \
num0,num1,num2,num3, \
num4,num5,num6,num7, \
num8,num7,num6,num5, \
num4,num3,num2,num1, \
num0,num1,num2,num3, \
num4,num5,num6,num7, \
num8,num7,num6,num5, \
num4,num3,num2,num1, \
num0,num1,num2,num3, \
num4,num5,num6,num7, \
num8,num7,num6,num5, \
num4,num3,num2,num1 \
}
 
// Device Class GUIDs
// {00000000-0000-0000-0000-000000000000}
#define DevClassReserved { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
// {D8745C7D-71A5-4977-A989-2E1EF853CCB8}
#define DevClassUnassigned { 0xd8745c7d, 0x71a5, 0x4977, { 0xa9, 0x89, 0x2e, 0x1e, 0xf8, 0x53, 0xcc, 0xb8 } }
// {15D37F46-A4F2-416d-8157-FAB7D1A66BE8}
#define DevClassServoController { 0x15d37f46, 0xa4f2, 0x416d, { 0x81, 0x57, 0xfa, 0xb7, 0xd1, 0xa6, 0x6b, 0xe8 } }
// {D3C8787E-DCE8-445b-B1C7-86C2BF85D144}
#define DevClassHBridge { 0xd3c8787e, 0xdce8, 0x445b, { 0xb1, 0xc7, 0x86, 0xc2, 0xbf, 0x85, 0xd1, 0x44 } }
 
// Device GUIDs
// {00000000-0000-0000-0000-000000000000}
#define DevReserved { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
// {E59F78CE-6328-4316-B1DE-F0AA5E45F34F}
#define DevUnassigned { 0xe59f78ce, 0x6328, 0x4316, { 0xb1, 0xde, 0xf0, 0xaa, 0x5e, 0x45, 0xf3, 0x4f } }
// {E3470D73-9110-4d61-915E-916FF8A89BA6}
#define DevUmServo32 { 0xe3470d73, 0x9110, 0x4d61, { 0x91, 0x5e, 0x91, 0x6f, 0xf8, 0xa8, 0x9b, 0xa6 } }
// {CF232A58-497F-4bba-9E13-D9AB7938B418}
#define DevUmHBridge { 0xcf232a58, 0x497f, 0x4bba, { 0x9e, 0x13, 0xd9, 0xab, 0x79, 0x38, 0xb4, 0x18 } }
// {518FC22C-B5A1-40b3-8D7C-C75EC729F25C}
#define DevUmServoBrain { 0x518fc22c, 0xb5a1, 0x40b3, { 0x8d, 0x7c, 0xc7, 0x5e, 0xc7, 0x29, 0xf2, 0x5c } }
 
#endif // __guids_h__
/Modules/HBRIDGE/HB2FET02A/SW/h-bridge.cpp
0,0 → 1,1338
// Features:
// - Complete PID loop
// - Optional proportional feed-forward for open-loop control and wind-up minimization
// - Wind-up limiting under overload conditions
// - Torque throttling under overload
// - TWI interface with PnP support
// - Parameter storage in EEPROM including TWI address
// - Acceleration, deceleration limits
// - Speed integration to estimate travelled distance
// - Add 'go to distance' mode with trapezoid speed-profiles - Needs testing
// - Added 32-bit and 16-bit atomic reads and writes. Note: 8-bit atomic writes are NOT supported
// - Added support for duty cycle throttling (basically torque-throttling)
// - Added current reading and max current detection
// - Added per cycle over-current detection and early-termination of cycle
// - Added servo-type operation with pot-based position feedback
 
// TODO:
// - Add current integration for power usage estimation
// - Add serial interface
// - Add servo-type (PWM) interface
// - Add optical encoder support
// - When sampling for fast-collapse high-side and during the on-state: we only need two states - the high-side should be on already
// - Switching between high- and low-collapse modes to equalize catch-diode load - make this user-selectable
// - Detect continous-current mode and do something about it!
 
// TODO TEST:
// - Servo mode in all four modes
// - Freewheeling and back-EMF in all four modes
// - Braking
 
// BUGS:
// - back-EMF measurment is different in fast-collapse and low-collapse modes. This make control leading to different speeds in the two modes.
 
#include <avr/pgmspace.h>
+#include <avr/sleep.h>
+
+#include "common.h"
+#include "opled.h"
+#include "usart.h"
+#include "twi_aap.h"
+#include "eeprom.h"
+
+#define H_BRIDGE
+
+void DebugStat();
+
+// This is an 8-bit PWM
+namespace PWM1 {
+ enum eClkSrc {
+ ClkNone = 0,
+ Clk = (1 << CS10),
+ ClkDiv1 = (1 << CS10),
+ ClkDiv8 = (1 << CS11),
+ ClkDiv64 = (1 << CS11) | (1 << CS10),
+ ClkDiv256 = (1 << CS12),
+ ClkDiv1024 = (1 << CS12) | (1 << CS10),
+ ClkExtFall = (1 << CS12) | (1 << CS11),
+ ClkExtRise = (1 << CS12) | (1 << CS11) | (1 << CS10)
+ };
+
+ void inline Init(eClkSrc aClkSrc) {
+ SETBIT(DDRB,0x02|0x04); // Preprare PortB to output to handle disables
+ CLRBIT(PORTB,0x02|0x04); // Set port bits to 0 so that disable will work correctly
+ CLRBIT(PRR,PRTIM0);
+ TCCR1A = (1 << WGM10);
+ TCCR1B = aClkSrc | (1 << WGM12);
+ OCR1A = 0x0000;
+ OCR1B = 0x0000;
+ ICR1 = 0x0000;
+ TCNT1 = 0x0000;
+ }
+ void inline DisableChA() { CLRBIT(TCCR1A,(1 << COM1A1)); }
+ void inline DisableChB() { CLRBIT(TCCR1A,(1 << COM1B1)); }
+ void inline DisableChAB() { CLRBIT(TCCR1A,(1 << COM1A1) | (1 << COM1B1)); }
+ void inline EnableChA() { SETBIT(TCCR1A,(1 << COM1A1)); }
+ void inline EnableChB() { SETBIT(TCCR1A,(1 << COM1B1)); }
+ void inline EnableChAB() { SETBIT(TCCR1A,(1 << COM1A1) | (1 << COM1B1)); }
+ bool inline IsChAEnabled() { return TESTBIT(TCCR1A,(1 << COM1A1)) != 0; }
+ bool inline IsChBEnabled() { return TESTBIT(TCCR1A,(1 << COM1B1)) != 0; }
+ bool inline IsChAOrBEnabled() { return TESTBIT(TCCR1A,(1 << COM1A1) | (1 << COM1B1)) != 0; }
+ // We never set the high bits to anything but 0, so the high-byte in the TEMP register doesn't need to be set.
+ void inline SetChannelA(uint8_t aValue) { OCR1AL = aValue; }
+ uint8_t inline GetChannelA() { return OCR1AL; }
+ void inline SetChannelB(uint8_t aValue) { OCR1BL = aValue; }
+ uint8_t inline GetChannelB() { return OCR1BL; }
+ void inline EnableIRQ_A() { SETBIT(TIMSK1,(1 << OCIE1A)); }
+ void inline EnableIRQ_B() { SETBIT(TIMSK1,(1 << OCIE1B)); }
+ void inline EnableIRQ_AB() { SETBIT(TIMSK1,(1 << OCIE1A) | (1 << OCIE1B)); }
+ void inline EnableIRQ_Overflow() { SETBIT(TIMSK1,(1 << TOIE1)); }
+ void inline EnableOnlyIRQ_A() { TIMSK1 = (1 << OCIE1A); }
+ void inline EnableOnlyIRQ_B() { TIMSK1 = (1 << OCIE1B); }
+ void inline EnableOnlyIRQ_Overflow() { TIMSK1 = (1 << TOIE1); }
+ void inline DisableIRQ_A() { CLRBIT(TIMSK1,(1 << OCIE1A)); }
+ void inline DisableIRQ_B() { CLRBIT(TIMSK1,(1 << OCIE1B)); }
+ void inline DisableIRQ_Overflow() { CLRBIT(TIMSK1,(1 << TOIE1)); }
+ void inline DisableIRQ_All() { TIMSK1 = 0; }
+ bool inline IsPendingIRQ_A() { return (TESTBIT(TIFR1,(1 << OCF1A)) != 0); }
+ bool inline IsPendingIRQ_B() { return (TESTBIT(TIFR1,(1 << OCF1B)) != 0); }
+ bool inline IsPendingIRQ_Overflow() { return (TESTBIT(TIFR1,(1 << TOV1)) != 0); }
+ void inline ClearPendingIRQ_A() { TIFR1 = (1 << OCF1A); }
+ void inline ClearPendingIRQ_B() { TIFR1 = (1 << OCF1B); }
+ void inline ClearPendingIRQ_Overflow() { TIFR1 = (1 << TOV1); }
+ void inline ClearPendingIRQ_All() { TIFR1 = (1 << OCF1A) | (1 << OCF1B) | (1 << TOV1); }
+}
+
+namespace ADConv {
+ enum eRef {
+ RefInt = (1 << REFS0) | (1 << REFS1),
+ RefVcc = (1 << REFS0),
+ RefExt = 0
+ };
+ enum eAdjust {
+ LeftAdjust = ADLAR,
+ RightAdjust = 0
+ };
+#if defined MEGA_BRIDGE
+ const uint8_t Ch_MotorA = 7;
+ const uint8_t Ch_MotorB = 2;
+ const uint8_t Ch_MotorCurrent = 6;
+ const uint8_t Ch_ServoPot = 3;
+ const uint8_t Ch_Battery = 1;
+
+ const eRef Ref_MotorA = RefVcc;
+ const eRef Ref_MotorB = RefVcc;
+ const eRef Ref_MotorCurrent = RefInt;
+ const eRef Ref_ServoPot = RefVcc;
+ const eRef Ref_Battery = RefVcc;
+#elif defined H_BRIDGE
+ const uint8_t Ch_MotorA = 7;
+ const uint8_t Ch_MotorB = 2;
+ const uint8_t Ch_MotorCurrent = 6;
+ const uint8_t Ch_ServoPot = 3;
+
+ const eRef Ref_MotorA = RefInt;
+ const eRef Ref_MotorB = RefInt;
+ const eRef Ref_MotorCurrent = RefInt;
+ const eRef Ref_ServoPot = RefVcc;
+ const eRef Ref_Battery = RefVcc;
+#elif defined SERVO_BRAIN
+ const uint8_t Ch_MotorA = 0;
+ const uint8_t Ch_MotorB = 2;
+ const uint8_t Ch_MotorCurrent = 6;
+ const uint8_t Ch_ServoPot = 3;
+
+ const eRef Ref_MotorA = RefVcc;
+ const eRef Ref_MotorB = RefVcc;
+ const eRef Ref_MotorCurrent = RefVcc;
+ const eRef Ref_ServoPot = RefVcc;
+ const eRef Ref_Battery = RefVcc;
+#else
+ #error HW version is not specified
+#endif
+
+ const uint8_t ADCSRA_BaseValue = (1 << ADEN) | (1 << ADATE) | (1 << ADPS2) | (1 << ADPS1);
+ static inline void Init() {
+ ADMUX = (1<< REFS0) | 15; // AVCC is the default reference
+ ADCSRA = ADCSRA_BaseValue;
+ ADCSRB = 0; // Free-running mode
+ DIDR0 = (1 << Ch_MotorA) | (1 << Ch_MotorB) | (1 << Ch_MotorCurrent);
+ }
+ static inline void SetChannel(uint8_t aChannel) {
+ ADMUX = (ADMUX & 0xf0) | (aChannel & 0x0f);
+ }
+ static inline void SetChannel(uint8_t aChannel,eRef aRef) {
+ ADMUX = (ADMUX & 0x20) | (aChannel & 0x0f) | aRef;
+ }
+ static inline void SetChannel(uint8_t aChannel,eRef aRef,eAdjust aAdjust) {
+ ADMUX = (aChannel & 0x0f) | aRef | aAdjust;
+ }
+ static inline void StartConversion(uint8_t aChannel) {
+ SetChannel(aChannel);
+ ADCSRA = ADCSRA_BaseValue | (1 << ADSC) | (1 << ADIE);
+ }
+ static inline void StartConversion(uint8_t aChannel,eRef aRef) {
+ SetChannel(aChannel,aRef);
+ ADCSRA = ADCSRA_BaseValue | (1 << ADSC) | (1 << ADIE);
+ }
+ static inline void StartConversion(uint8_t aChannel,eRef aRef, eAdjust aAdjust) {
+ SetChannel(aChannel,aRef,aAdjust);
+ ADCSRA = ADCSRA_BaseValue | (1 << ADSC) | (1 << ADIE);
+ }
+ static inline void StopConversion() {
+ CLRBIT(ADCSRA,(1 << ADEN)); // This might delete the pending IRQ request, but we don't really care
+ }
+ static inline uint8_t GetChannel() {
+ return ADMUX & 0x0f;
+ }
+ static uint16_t GetSample() {
+ while((ADCSRA & (1 << ADIF)) == 0); // Wait until conversion finishes
+ uint16_t RetVal = ADC;
+ SETBIT(ADCSRA,ADIF); // Clear the pending interrupt request (though we're polling)
+ return RetVal;
+ }
+ static inline uint16_t FastGetSample() {
+ return ADC;
+ }
+}
+
+namespace EEPROM_layout {
+ const uint16_t DataRecord_Ofs = 0x00; // size is sizeof(HBridge::DataRecord)
+ const uint16_t DataValid_Ofs = 0xfe;
+}
+
+template<typename T> inline T min(T aA,T aB) { return (aA>aB)?aB:aA; }
+template<typename T> inline T max(T aA,T aB) { return (aA>aB)?aA:aB; }
+
+#define SHOOT_THROUGH_DELAY for(int i=0;i<10000;++i);
+
+namespace HBridge {
+ enum eSampleStates {
+ SampleState_PreFastCollapse = 0,
+ SampleState_FastCollapse,
+ SampleState_PostFastCollapse,
+
+ SampleState_PreSampleBase,
+ SampleState_PreSampleBase2,
+ SampleState_SampleBase,
+ SampleState_PreSearchMax,
+ SampleState_PreSamplePot,
+ SampleState_SamplePot,
+ SampleState_SearchMax,
+ SampleState_SearchMin,
+ SampleState_PreCurrentSample,
+ SampleState_CurrentSample1,
+ SampleState_CurrentSample2,
+ SampleState_CurrentSample3,
+ // off-band battery sampling for fast-high-side collapse mode
+ SampleState_PreSampleBat,
+ SampleState_SampleBat
+ };
+ enum eOperatingModes {
+ OperatingMode_Speed = 0,
+ OperatingMode_Servo = 1
+ };
+
+ const int16_t RequestFreewheel = 0x4000;
+ const int16_t RequestBrake = 0x4001;
+
+ const uint8_t EEPROMDataValid = 0x01;
+
+ const uint8_t GuardTime = 255 - 47; // Maximum allowed duty cycle
+ const uint8_t ControlTime = 255 - 10;
+#if defined MEGA_BRIDGE
+ const uint8_t LoBMask = (0x01 << 2);
+ const uint8_t LoAMask = (0x01 << 1);
+ const uint8_t HiBMask = (0x01 << 5);
+ const uint8_t HiAMask = (0x01 << 6);
+ const eOperatingModes DefaultOperatingMode = OperatingMode_Speed;
+
+ const int16_t Def_IFactor = 0;
+ const int16_t Def_PFactor = 0;
+ const int16_t Def_DFactor = 0;
+ const int16_t Def_PFFactor = 0x0100;
+#elif defined H_BRIDGE
+ const uint8_t LoBMask = (0x01 << 2);
+ const uint8_t LoAMask = (0x01 << 1);
+ const uint8_t HiBMask = (0x01 << 5);
+ const uint8_t HiAMask = (0x01 << 6);
+ const eOperatingModes DefaultOperatingMode = OperatingMode_Speed;
+
+ const int16_t Def_IFactor = 0;
+ const int16_t Def_PFactor = 0;
+ const int16_t Def_DFactor = 0;
+ const int16_t Def_PFFactor = 0x0100;
+#elif defined SERVO_BRAIN
+ const uint8_t LoBMask = (0x01 << 2);
+ const uint8_t LoAMask = (0x01 << 1);
+ const uint8_t HiBMask = (0x01 << 6);
+ const uint8_t HiAMask = (0x01 << 5);
+ const eOperatingModes DefaultOperatingMode = OperatingMode_Servo;
+
+ const int16_t Def_IFactor = 0;
+ const int16_t Def_PFactor = 0x0200;
+ const int16_t Def_DFactor = 0;
+ const int16_t Def_PFFactor = 0;
+#else
+#error No HW version is specified!
+#endif
+
+ enum CollapseStates {
+ FastCollapseHighSide = 0,
+ FastCollapseLowSide,
+ SlowCollapseHighSide,
+ SlowCollapseLowSide,
+ CollapseStateMask = 0x0f,
+ CollapseStateAutoCycle = 0x10
+ };
+
+ uint8_t ADSampleIdx;
+ struct PublicData {
+ // The layout of this record IS important.
+ // This is the public interface that is accessible through the TWI interface
+
+ // Speed control request signal R/W (Scaled between -0x3fff and 0x3fff
+ // 0x4000 is freewheeling and 0x4001 is braking
+ int16_t RequestValue;
+
+ // PID control loop parameters R/W
+ int16_t IFactor;
+ int16_t PFactor;
+ int16_t DFactor;
+ int16_t PFFactor;
+ int16_t SampleOffset;
+
+ // Request change limits (acceleration limits) R/W
+ int16_t MaxPositiveChange;
+ int16_t MaxNegativeChange;
+
+ // Travel (distance counter) R/W
+ int32_t Distance;
+
+ // Travel cutoff R/W
+ int32_t FwDistanceLimit;
+ int32_t BwDistanceLimit;
+
+ // Current estimated distance required to stop (R/O)
+ int32_t DistanceToStop;
+
+ // Modified request (R/O)
+ int16_t CurrentRequest;
+
+ // Command given to the H-bridge R/O
+ int16_t Command;
+
+ // PID loop working set R/O
+ int16_t IValue;
+ int16_t LastError;
+ int16_t Error;
+
+ // Last Back-EMF sample R/O
+ int16_t VoltageSample;
+
+ // Back-EMF sampling code working set R/O
+ int16_t BaseValue;
+
+ int16_t SampleCnt_Snapshot;
+ int16_t MinValue_Snapshot;
+
+ // Private members: not part of the communication interface, but added to this structure for potential debugging
+ int16_t MinValue;
+ int16_t MaxValue;
+ int16_t SampleCnt;
+
+ int16_t OriginalRequestValue;
+
+ uint8_t SampleState;
+ uint8_t DutyCycleThrottle;
+ uint8_t NewData;
+ uint8_t IsForward;
+
+ uint16_t CurrentMax;
+ uint16_t CurrentDelta;
+ uint16_t CurrentTemp;
+ uint16_t CurrentMaxSearch;
+
+ uint8_t ADBufferEnable;
+ uint8_t ADBufferEnableHost;
+
+ uint16_t CurrentLimit;
+
+ uint8_t OperatingMode;
+ uint8_t CollapseState;
+
+ uint16_t ADBuffer[80];
+ } DataRecord;
+
+ inline uint8_t* GetDataRecord8(uint8_t aOfs) { return ((uint8_t *)&DataRecord)+aOfs; }
+ inline uint16_t* GetDataRecord16(uint8_t aOfs) { return (uint16_t *)(((uint8_t *)&DataRecord)+aOfs); }
+ inline uint32_t* GetDataRecord32(uint8_t aOfs) { return (uint32_t *)(((uint8_t *)&DataRecord)+aOfs); }
+ const inline size_t GetDataRecordSize() { return sizeof(DataRecord); }
+ uint8_t GetDataElementSize(uint8_t aOfs) {
+ #define FIELD_OFFSET(aField) ((uint8_t)((size_t)&(HBridge::DataRecord.aField)-(size_t)&HBridge::DataRecord))
+ #define OFFSET_ENTRY(aField) if (aOfs >= FIELD_OFFSET(aField)) return sizeof(DataRecord.aField); else
+ #define OFFSET_ENTRY_2(aField,aSize) if (aOfs >= FIELD_OFFSET(aField)) return (aSize); else
+ OFFSET_ENTRY_2(ADBuffer,sizeof(DataRecord.ADBuffer[0]))
+ OFFSET_ENTRY(CollapseState)
+ OFFSET_ENTRY(OperatingMode)
+ OFFSET_ENTRY(CurrentLimit)
+ OFFSET_ENTRY(ADBufferEnableHost)
+ OFFSET_ENTRY(ADBufferEnable)
+ OFFSET_ENTRY(CurrentMaxSearch)
+ OFFSET_ENTRY(CurrentTemp)
+ OFFSET_ENTRY(CurrentDelta)
+ OFFSET_ENTRY(CurrentMax)
+ OFFSET_ENTRY(IsForward)
+ OFFSET_ENTRY(NewData)
+ OFFSET_ENTRY(DutyCycleThrottle)
+ OFFSET_ENTRY(SampleState)
+ OFFSET_ENTRY(OriginalRequestValue)
+ OFFSET_ENTRY(SampleCnt)
+ OFFSET_ENTRY(MaxValue)
+ OFFSET_ENTRY(MinValue)
+ OFFSET_ENTRY(MinValue_Snapshot)
+ OFFSET_ENTRY(SampleCnt_Snapshot)
+ OFFSET_ENTRY(BaseValue)
+ OFFSET_ENTRY(VoltageSample)
+ OFFSET_ENTRY(Error)
+ OFFSET_ENTRY(LastError)
+ OFFSET_ENTRY(IValue)
+ OFFSET_ENTRY(Command)
+ OFFSET_ENTRY(CurrentRequest)
+ OFFSET_ENTRY(DistanceToStop)
+ OFFSET_ENTRY(BwDistanceLimit)
+ OFFSET_ENTRY(FwDistanceLimit)
+ OFFSET_ENTRY(Distance)
+ OFFSET_ENTRY(MaxNegativeChange)
+ OFFSET_ENTRY(MaxPositiveChange)
+ OFFSET_ENTRY(SampleOffset)
+ OFFSET_ENTRY(PFFactor)
+ OFFSET_ENTRY(DFactor)
+ OFFSET_ENTRY(PFactor)
+ OFFSET_ENTRY(IFactor)
+ OFFSET_ENTRY(RequestValue)
+ return 1;
+ #undef FIELD_OFFSET
+ #undef OFFSET_ENTRY
+ #undef OFFSET_ENTRY2
+ }
+
+ void FreeWheel();
+
+ inline void SaveSettings() {
+ uint8_t *Data = (uint8_t *)&DataRecord;
+ for(uint8_t i=EEPROM_layout::DataRecord_Ofs;i<EEPROM_layout::DataRecord_Ofs+sizeof(DataRecord);++i,++Data) {
+ EEPROM::SetByte(i,*Data);
+ }
+ EEPROM::SetByte(EEPROM_layout::DataValid_Ofs,EEPROMDataValid);
+ EEPROM::Wait();
+ }
+
+ inline void Init() {
+ // Init to all channels off - this is the free-wheeling state
+ PWM1::Init(PWM1::ClkDiv256); // Set clock to clkI/O / 256 -> full cycle is around 120Hz with an 8MHz clock
+ //PWM1::Init(PWM1::ClkDiv64); // Set clock to clkI/O / 64 -> full cycle is around 500Hz with an 8MHz clock
+ CLRBIT(PORTD,HiAMask);
+ CLRBIT(PORTD,HiBMask);
+ CLRBIT(PORTB,LoBMask);
+ CLRBIT(PORTB,LoBMask);
+ SETBIT(DDRB,LoBMask);
+ SETBIT(DDRB,LoAMask);
+ SETBIT(DDRD,HiBMask);
+ SETBIT(DDRD,HiAMask);
+
+ // No control loop - this is the default.
+ DataRecord.IFactor = Def_IFactor;
+ DataRecord.PFactor = Def_PFactor;
+ DataRecord.DFactor = Def_DFactor;
+ DataRecord.PFFactor = Def_PFFactor;
+ DataRecord.SampleOffset = 0;
+ DataRecord.CollapseState = FastCollapseHighSide;
+ DataRecord.OperatingMode = DefaultOperatingMode;
+ // No acceleration control - this is the default.
+ DataRecord.MaxPositiveChange = 0x7fff;
+ DataRecord.MaxNegativeChange = 0x7fff;
+
+ if (EEPROM::GetByte(EEPROM_layout::DataValid_Ofs) == EEPROMDataValid) {
+ uint8_t *Data = (uint8_t *)&DataRecord;
+ for(uint8_t i=EEPROM_layout::DataRecord_Ofs;i<EEPROM_layout::DataRecord_Ofs+sizeof(DataRecord);++i,++Data) {
+ *Data = EEPROM::GetByte(i);
+ }
+ }
+
+ DataRecord.IValue = 0;
+ DataRecord.Error = 0;
+ DataRecord.LastError = 0;
+ DataRecord.RequestValue = RequestFreewheel;
+ DataRecord.OriginalRequestValue = RequestFreewheel;
+ DataRecord.IsForward = true;
+ DataRecord.NewData = false;
+ DataRecord.Distance = 0;
+ DataRecord.FwDistanceLimit = 0x7fffffff;
+ DataRecord.BwDistanceLimit = 0x80000000;
+ DataRecord.DutyCycleThrottle = GuardTime;
+ DataRecord.CurrentLimit = 0xffff; // Anything over 0x3ff is OFF
+ for(uint8_t i = 0;i<sizeof(DataRecord.ADBuffer)/sizeof(DataRecord.ADBuffer[0]);++i) {
+ DataRecord.ADBuffer[i] = 0;
+ }
+ ADSampleIdx = 0;
+ DataRecord.ADBufferEnable = 0;
+ DataRecord.ADBufferEnableHost = true;
+
+ FreeWheel();
+ PWM1::EnableIRQ_AB();
+
+ DataRecord.SampleState = SampleState_CurrentSample3;
+ ADConv::StartConversion(ADConv::Ch_MotorCurrent);
+ }
+ // If made inline GCC generates invalid code
+ void Forward(uint8_t aSpeed) {
+ if (aSpeed > DataRecord.DutyCycleThrottle) aSpeed = DataRecord.DutyCycleThrottle;
+ if (aSpeed > GuardTime) aSpeed = GuardTime;
+ // Allways clear first, than set
+ CLRBIT(PORTB,LoBMask);
+ CLRBIT(PORTB,LoAMask);
+ CLRBIT(PORTD,HiBMask);
+ SETBIT(PORTD,HiAMask);
+ PWM1::SetChannelB(aSpeed);
+ PWM1::SetChannelA(ControlTime);
+ PWM1::DisableChAB();
+ if (aSpeed > 0) PWM1::EnableChB();
+ DataRecord.IsForward = true;
+ }
+ void Backward(uint8_t aSpeed) {
+ if (aSpeed > DataRecord.DutyCycleThrottle) aSpeed = DataRecord.DutyCycleThrottle;
+ if (aSpeed > GuardTime) aSpeed = GuardTime;
+ // Allways clear first, than set
+ CLRBIT(PORTB,LoBMask);
+ CLRBIT(PORTB,LoAMask);
+ CLRBIT(PORTD,HiAMask);
+ SETBIT(PORTD,HiBMask);
+ PWM1::SetChannelA(aSpeed);
+ PWM1::SetChannelB(ControlTime);
+ PWM1::DisableChAB();
+ if (aSpeed > 0) PWM1::EnableChA();
+ DataRecord.IsForward = false;
+ }
+ inline void FastFieldCollapseHighSide() {
+ if (PWM1::IsChAEnabled()) {
+ CLRBIT(PORTD,HiBMask);
+ CLRBIT(PORTD,HiAMask);
+ SETBIT(PORTD,HiAMask);
+ } else if (PWM1::IsChBEnabled()) {
+ CLRBIT(PORTD,HiAMask);
+ CLRBIT(PORTD,HiBMask);
+ SETBIT(PORTD,HiBMask);
+ }
+ }
+ inline void FastFieldCollapseLowSide() {
+ if (PWM1::IsChAEnabled()) {
+ CLRBIT(PORTD,HiAMask);
+ CLRBIT(PORTD,HiBMask);
+ SHOOT_THROUGH_DELAY;
+ CLRBIT(PORTB,LoAMask);
+ PWM1::DisableChAB();
+ SETBIT(PORTB,LoBMask);
+ } else if (PWM1::IsChBEnabled()) {
+ CLRBIT(PORTD,HiAMask);
+ CLRBIT(PORTD,HiBMask);
+ SHOOT_THROUGH_DELAY;
+ CLRBIT(PORTB,LoBMask);
+ PWM1::DisableChAB();
+ SETBIT(PORTB,LoAMask);
+ }
+ }
+ inline void SlowFieldCollapseHighSide() {
+ }
+ inline void SlowFieldCollapseLowSide() {
+ if (PWM1::IsChAEnabled()) {
+ CLRBIT(PORTD,HiAMask);
+ CLRBIT(PORTD,HiBMask);
+ SHOOT_THROUGH_DELAY;
+ CLRBIT(PORTB,LoBMask);
+ PWM1::DisableChAB();
+ SETBIT(PORTB,LoAMask);
+ } else if (PWM1::IsChBEnabled()) {
+ CLRBIT(PORTD,HiAMask);
+ CLRBIT(PORTD,HiBMask);
+ SHOOT_THROUGH_DELAY;
+ CLRBIT(PORTB,LoAMask);
+ PWM1::DisableChAB();
+ SETBIT(PORTB,LoBMask);
+ }
+ }
+ inline void ResetAfterFastCollapse() {
+ if (TESTBIT(PORTD,HiAMask) != 0) {
+ CLRBIT(PORTD,HiAMask);
+ SETBIT(PORTD,HiBMask);
+ } else if (TESTBIT(PORTD,HiBMask) != 0) {
+ CLRBIT(PORTD,HiBMask);
+ SETBIT(PORTD,HiAMask);
+ } else if (TESTBIT(PORTB,LoAMask) != 0) {
+ CLRBIT(PORTB,LoAMask);
+ SETBIT(PORTB,LoBMask);
+ } else if (TESTBIT(PORTB,LoBMask) != 0) {
+ CLRBIT(PORTB,LoBMask);
+ SETBIT(PORTB,LoAMask);
+ }
+ }
+
+ void CollapseField() {
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseHighSide:
+ FastFieldCollapseHighSide();
+ break;
+ case FastCollapseLowSide:
+ FastFieldCollapseLowSide();
+ break;
+ default:
+ case SlowCollapseHighSide:
+ SlowFieldCollapseHighSide();
+ break;
+ case SlowCollapseLowSide:
+ SlowFieldCollapseLowSide();
+ break;
+ }
+ }
+
+ inline void SwitchCollapseType() {
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseHighSide:
+ if (TESTBIT(DataRecord.CollapseState,CollapseStateAutoCycle) != 0) {
+ DataRecord.CollapseState = CollapseStateAutoCycle | FastCollapseLowSide;
+ }
+ break;
+ case FastCollapseLowSide:
+ if (TESTBIT(DataRecord.CollapseState,CollapseStateAutoCycle) != 0) {
+ DataRecord.CollapseState = CollapseStateAutoCycle | FastCollapseHighSide;
+ }
+ break;
+ default:
+ case SlowCollapseHighSide:
+ if (TESTBIT(DataRecord.CollapseState,CollapseStateAutoCycle) != 0) {
+ DataRecord.CollapseState = CollapseStateAutoCycle | SlowCollapseLowSide;
+ } else {
+ DataRecord.CollapseState = SlowCollapseHighSide;
+ }
+ break;
+ case SlowCollapseLowSide:
+ if (TESTBIT(DataRecord.CollapseState,CollapseStateAutoCycle) != 0) {
+ DataRecord.CollapseState = CollapseStateAutoCycle | SlowCollapseHighSide;
+ }
+ break;
+ }
+ }
+ inline void ResetHighSide() {
+ if (TESTBIT(PORTD,HiBMask)) {
+ CLRBIT(PORTD,HiBMask);
+ SETBIT(PORTD,HiBMask);
+ }
+ if (TESTBIT(PORTD,HiAMask)) {
+ CLRBIT(PORTD,HiAMask);
+ SETBIT(PORTD,HiAMask);
+ }
+ }
+ void FreeWheel() {
+ // Disable everything
+ CLRBIT(PORTB,LoBMask);
+ CLRBIT(PORTB,LoAMask);
+ CLRBIT(PORTD,HiBMask);
+ CLRBIT(PORTD,HiAMask);
+ PWM1::DisableChAB();
+ // Set up interrupts to some reasonable values
+ if (!DataRecord.IsForward) {
+ PWM1::SetChannelA(0x10);
+ PWM1::SetChannelB(ControlTime);
+ } else {
+ PWM1::SetChannelB(0x10);
+ PWM1::SetChannelA(ControlTime);
+ }
+ }
+ void Brake() {
+ // Allways clear first, than set
+ CLRBIT(PORTB,LoBMask);
+ CLRBIT(PORTB,LoAMask);
+ PWM1::DisableChAB();
+ PWM1::SetChannelB(0x10); // Set it to some reasonable value
+ PWM1::SetChannelA(ControlTime);
+ DataRecord.IsForward = true;
+ SETBIT(PORTD,HiAMask);
+ SETBIT(PORTD,HiBMask);
+ }
+ void HandleOverload() {
+ // Turn off both low-side FETs - this will remove the load for the rest of the cycle
+ CLRBIT(PORTB,LoBMask);
+ CLRBIT(PORTB,LoAMask);
+ PWM1::DisableChAB();
+ }
+
+
+ int16_t ScaledMult(int16_t aA, int16_t aB) {
+ return (((int32_t)aA * (int32_t)aB) >> 8);
+ }
+
+ static inline void DoControl() {
+ // Control acceleration
+ // Note: DoControl will not be called if RequestValue is Freewheel or Braking
+ int16_t SpeedDiff = DataRecord.RequestValue - DataRecord.CurrentRequest;
+ if (SpeedDiff > DataRecord.MaxPositiveChange) {
+ DataRecord.CurrentRequest += DataRecord.MaxPositiveChange;
+ } else if (SpeedDiff < -DataRecord.MaxNegativeChange) {
+ DataRecord.CurrentRequest -= DataRecord.MaxNegativeChange;
+ } else {
+ DataRecord.CurrentRequest = DataRecord.RequestValue;
+ }
+
+ // Limit motion to travel cutoff values. Note that we update RequestValue and not CurrentRequest
+ // so stop will be smooth. Since we estimage the time required to stop this also implements
+ // the go-to-distance functionality.
+ int16_t Change = (DataRecord.OriginalRequestValue > 0)?DataRecord.MaxNegativeChange:-DataRecord.MaxPositiveChange;
+ DataRecord.DistanceToStop = ((int32_t)DataRecord.VoltageSample * (int32_t)DataRecord.VoltageSample / (int32_t)Change) << 3;
+ int32_t StopPosition = DataRecord.Distance + DataRecord.DistanceToStop;
+ if (DataRecord.OriginalRequestValue > 0) {
+ if (StopPosition > DataRecord.FwDistanceLimit) {
+ DataRecord.RequestValue = DataRecord.CurrentRequest - min(DataRecord.MaxNegativeChange,DataRecord.CurrentRequest);
+ }
+ } else {
+ if (StopPosition < DataRecord.BwDistanceLimit) {
+ DataRecord.RequestValue = DataRecord.CurrentRequest + min(DataRecord.MaxPositiveChange,-DataRecord.CurrentRequest);
+ }
+ }
+
+ // Control loop
+ int16_t ScaledRequest = DataRecord.CurrentRequest >> 4;
+ DataRecord.LastError = DataRecord.Error;
+ DataRecord.Error = DataRecord.VoltageSample - ScaledRequest;
+ int16_t DValue = DataRecord.Error - DataRecord.LastError;
+ DataRecord.Command = ScaledMult(ScaledRequest,DataRecord.PFFactor) + ScaledMult(DataRecord.IValue,DataRecord.IFactor) + ScaledMult(DataRecord.Error,DataRecord.PFactor) + ScaledMult(DValue,DataRecord.DFactor);
+ // Limit command to valid range and limit IValue growth as well
+ if (DataRecord.Command >= 0x100) {
+ DataRecord.Command = 0xffL;
+ // In an overflow case allow integrator value updates if it works against the overflow (sign bits are differenet)
+ if (((DataRecord.IValue ^ DataRecord.Error) & 0x8000) != 0) DataRecord.IValue += DataRecord.Error;
+ } else if (DataRecord.Command <= -0x100) {
+ DataRecord.Command = -0xffL;
+ // In an overflow case allow integrator value updates if it works against the overflow (sign bits are differenet)
+ if (((DataRecord.IValue ^ DataRecord.Error) & 0x8000) != 0) DataRecord.IValue += DataRecord.Error;
+ } else {
+ // Use saturated arithmetics to avoid roll-over in the accumulator
+ int32_t TempIValue = (int32_t)DataRecord.IValue + (int32_t)DataRecord.Error;
+ if (TempIValue > 0x7fff) {
+ DataRecord.IValue = 0x7fffL;
+ } else if (TempIValue < -0x7fffL) {
+ DataRecord.IValue = -0x7fff;
+ } else {
+ DataRecord.IValue = TempIValue;
+ }
+ }
+ if (DataRecord.Command > 0) Forward(DataRecord.Command); else Backward((-DataRecord.Command));
+
+ DataRecord.NewData = true;
+ }
+
+ uint8_t SampleStateCnt = 0;
+
+ inline bool BatSampleWhileOn() {
+ uint8_t OnTime = (DataRecord.IsForward)?PWM1::GetChannelB():PWM1::GetChannelA();
+ return (OnTime > 128);
+ }
+
+ // Samples the positive pole of the motor WRT back-EMF
+ void SetADChannelMotorPositive() {
+ if (DataRecord.IsForward) {
+ ADConv::SetChannel(ADConv::Ch_MotorA,ADConv::Ref_MotorA,ADConv::RightAdjust);
+ } else {
+ ADConv::SetChannel(ADConv::Ch_MotorB,ADConv::Ref_MotorB,ADConv::RightAdjust);
+ }
+ }
+
+ // Samples the negative pole of the motor WRT back-EMF
+ void SetADChannelMotorNegative() {
+ if (DataRecord.IsForward) {
+ ADConv::SetChannel(ADConv::Ch_MotorB,ADConv::Ref_MotorB,ADConv::RightAdjust);
+ } else {
+ ADConv::SetChannel(ADConv::Ch_MotorA,ADConv::Ref_MotorA,ADConv::RightAdjust);
+ }
+ }
+
+ static void StartOffPhase(bool aWasChAOrBEnabled) {
+ // Start new measurements
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseLowSide:
+ SetADChannelMotorNegative();
+ if (aWasChAOrBEnabled) {
+ DataRecord.SampleState = SampleState_PreFastCollapse;
+ } else {
+ DataRecord.SampleState = SampleState_PostFastCollapse;
+ }
+ break;
+ case FastCollapseHighSide:
+ SetADChannelMotorPositive();
+ if (aWasChAOrBEnabled) {
+ DataRecord.SampleState = SampleState_PreFastCollapse;
+ } else {
+ DataRecord.SampleState = SampleState_PostFastCollapse;
+ }
+ break;
+ case SlowCollapseLowSide:
+ SetADChannelMotorNegative();
+ DataRecord.SampleState = SampleState_PreSampleBase;
+ break;
+ case SlowCollapseHighSide:
+ SetADChannelMotorPositive();
+ DataRecord.SampleState = SampleState_PreSampleBase;
+ break;
+ }
+ }
+
+ static inline void StartOnPhase() {
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseLowSide:
+ ADConv::SetChannel(ADConv::Ch_MotorCurrent,ADConv::Ref_MotorCurrent,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreCurrentSample;
+ break;
+ case FastCollapseHighSide:
+ if (BatSampleWhileOn()) {
+ SetADChannelMotorPositive();
+ DataRecord.SampleState = SampleState_PreSampleBat;
+ SampleStateCnt = 0;
+ } else {
+ ADConv::SetChannel(ADConv::Ch_MotorCurrent,ADConv::Ref_MotorCurrent,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreCurrentSample;
+ }
+ break;
+ case SlowCollapseLowSide:
+ ADConv::SetChannel(ADConv::Ch_MotorCurrent,ADConv::Ref_MotorCurrent,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreCurrentSample;
+ break;
+ case SlowCollapseHighSide:
+ ADConv::SetChannel(ADConv::Ch_MotorCurrent,ADConv::Ref_MotorCurrent,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreCurrentSample;
+ break;
+ }
+ bool InvertSample = !DataRecord.IsForward;
+ switch (DataRecord.OperatingMode) {
+ default:
+ case OperatingMode_Speed: {
+ int16_t LocalBaseValue = DataRecord.BaseValue;
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseHighSide:
+ case SlowCollapseHighSide:
+ break;
+ case FastCollapseLowSide:
+ case SlowCollapseLowSide:
+ InvertSample = !InvertSample;
+ LocalBaseValue = 0; // These are ground-based measurements...
+ break;
+ }
+ if (!InvertSample) {
+ DataRecord.VoltageSample = LocalBaseValue - DataRecord.MinValue;
+ } else {
+ DataRecord.VoltageSample = DataRecord.MinValue - LocalBaseValue;
+ }
+ }
+ break;
+ case OperatingMode_Servo:
+ DataRecord.VoltageSample = DataRecord.MaxValue;
+ break;
+ }
+
+ DataRecord.VoltageSample -= DataRecord.SampleOffset;
+ DataRecord.SampleCnt_Snapshot = DataRecord.SampleCnt;
+ DataRecord.MinValue_Snapshot = DataRecord.MinValue;
+ }
+
+ uint8_t BlinkCnt;
+ const uint8_t BlinkInterval = 50;
+
+ void HandleIRQ(bool IsIRQA) {
+ if (DataRecord.IsForward == IsIRQA) {
+ // Almost at the end: grab back-EMF data from sampling,
+ // do the control, and start sampling for current
+ StartOnPhase();
+
+ switch (DataRecord.RequestValue) {
+ case RequestFreewheel:
+ FreeWheel();
+ DataRecord.CurrentRequest = 0;
+ break;
+ case RequestBrake:
+ Brake();
+ DataRecord.CurrentRequest = 0;
+ break;
+ default:
+ // Update the travalled distance:
+ DataRecord.Distance += DataRecord.VoltageSample;
+ DoControl();
+ break;
+ }
+ SwitchCollapseType();
+ // Toggle user LED
+ ++BlinkCnt;
+ if (BlinkCnt > BlinkInterval) {
+ BlinkCnt = 0;
+ OpLed::Toggle();
+ }
+ } else {
+ // At the end of the on-part: reverse voltage across motor to fast-discharge it.
+ bool WasChAOrBEnabled = PWM1::IsChAOrBEnabled();
+ CollapseField();
+
+ ADSampleIdx = 0;
+
+ if (DataRecord.ADBufferEnable == 0) {
+ DataRecord.ADBufferEnable = 1;
+ }
+ if (DataRecord.ADBufferEnableHost) {
+ DataRecord.ADBufferEnableHost = false;
+ DataRecord.ADBufferEnable = 0;
+ }
+
+ // Start sampling for voltages and back-EMF
+ StartOffPhase(WasChAOrBEnabled);
+ ResetHighSide();
+ }
+ }
+
+ uint8_t CurrentSampleCnt;
+ const uint8_t CollapseSearchBlank = 1;
+ const uint16_t CollapseSearchLowLimit = 0x30;
+ const uint16_t CollapseSearchHighLimit = 0x90;
+
+ void Sample() {
+ int16_t CurData = ADConv::FastGetSample();
+
+ // Save off samples in the ADBuffer for host-side 'scope' display.
+ if (DataRecord.ADBufferEnable == 0) {
+ if (ADSampleIdx < sizeof(DataRecord.ADBuffer)/sizeof(DataRecord.ADBuffer[0])) {
+ int16_t ADSampleData = (int16_t)ADConv::GetChannel() << 10;
+ ADSampleData |= CurData;
+ DataRecord.ADBuffer[ADSampleIdx] = ADSampleData;
+ ++ADSampleIdx;
+ if (ADSampleIdx < sizeof(DataRecord.ADBuffer)/sizeof(DataRecord.ADBuffer[0])) {
+ DataRecord.ADBuffer[ADSampleIdx] = 0;
+ }
+ TOGGLEBIT(PORTD,0x10);
+ }
+ }
+
+ // State-machine for data-sampling.
+ switch (DataRecord.SampleState) {
+ case SampleState_PreFastCollapse:
+ DataRecord.SampleState = SampleState_FastCollapse;
+ // Update max current from the search field
+ DataRecord.CurrentMax = DataRecord.CurrentMaxSearch;
+ // TODO: If DataRecord.CurrentTemp isn't DataRecord.CurrentMaxSearch (more or less)
+ // than the over-current protection must have been activated.
+ SampleStateCnt = 0;
+ break;
+ case SampleState_FastCollapse:
+ // Wait in this state until turn-off transient is over
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseLowSide:
+ if (CurData < CollapseSearchLowLimit && SampleStateCnt > CollapseSearchBlank) {
+ ResetAfterFastCollapse();
+ DataRecord.SampleState = SampleState_PostFastCollapse;
+ } else if (CurData > CollapseSearchHighLimit) {
+ SampleStateCnt = CollapseSearchBlank;
+ }
+ ++SampleStateCnt;
+ break;
+ case FastCollapseHighSide:
+ if (CurData > CollapseSearchHighLimit && SampleStateCnt > CollapseSearchBlank) {
+ ResetAfterFastCollapse();
+ DataRecord.SampleState = SampleState_PostFastCollapse;
+ } else if (CurData < CollapseSearchLowLimit) {
+ SampleStateCnt = CollapseSearchBlank;
+ }
+ ++SampleStateCnt;
+ break;
+ }
+ break;
+ case SampleState_PostFastCollapse:
+ switch (DataRecord.OperatingMode) {
+ default:
+ case OperatingMode_Speed:
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseLowSide:
+ SetADChannelMotorPositive();
+ DataRecord.BaseValue = CurData;
+ DataRecord.SampleState = SampleState_PreSearchMax;
+ break;
+ case FastCollapseHighSide:
+ if (BatSampleWhileOn()) {
+ SetADChannelMotorNegative();
+ DataRecord.SampleState = SampleState_PreSearchMax;
+ } else {
+ SetADChannelMotorPositive();
+ DataRecord.SampleState = SampleState_PreSampleBat;
+ SampleStateCnt = 0;
+ }
+ break;
+ }
+ break;
+ case OperatingMode_Servo:
+ ADConv::SetChannel(ADConv::Ch_ServoPot,ADConv::Ref_ServoPot,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreSamplePot;
+ break;
+ }
+ break;
+ case SampleState_PreSampleBase:
+ // Throw away the data, but the next one is for real!
+ DataRecord.SampleState = SampleState_PreSampleBase2;
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case SlowCollapseLowSide:
+ SetADChannelMotorNegative();
+ break;
+ case SlowCollapseHighSide:
+ SetADChannelMotorPositive();
+ break;
+ }
+ // Update max current from the search field
+ DataRecord.CurrentMax = DataRecord.CurrentMaxSearch;
+ // TODO: If DataRecord.CurrentTemp isn't DataRecord.CurrentMaxSearch (more or less)
+ // than the over-current protection must have been activated.
+ SampleStateCnt = 0;
+ break;
+ case SampleState_PreSampleBase2:
+ // throw away this data, but the next one is for real!
+ DataRecord.SampleState = SampleState_SampleBase;
+ break;
+ case SampleState_SampleBase:
+ switch (DataRecord.OperatingMode) {
+ default:
+ case OperatingMode_Speed:
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case SlowCollapseLowSide:
+ SetADChannelMotorPositive();
+ DataRecord.BaseValue = CurData;
+ DataRecord.SampleState = SampleState_PreSearchMax;
+ break;
+ case SlowCollapseHighSide:
+ SetADChannelMotorNegative();
+ DataRecord.BaseValue = CurData;
+ DataRecord.SampleState = SampleState_PreSearchMax;
+ break;
+ }
+ break;
+ case OperatingMode_Servo:
+ ADConv::SetChannel(ADConv::Ch_ServoPot,ADConv::Ref_ServoPot,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreSamplePot;
+ break;
+ }
+ break;
+ case SampleState_PreSamplePot:
+ DataRecord.SampleState = SampleState_SamplePot;
+ break;
+ case SampleState_SamplePot:
+ DataRecord.MaxValue = CurData;
+ DataRecord.SampleCnt = 1;
+ break;
+ case SampleState_PreSearchMax:
+ DataRecord.SampleState = SampleState_SearchMax;
+ DataRecord.MaxValue = 0x0000;
+ DataRecord.SampleCnt = 0;
+ break;
+ case SampleState_SearchMax:
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseHighSide:
+ case SlowCollapseLowSide:
+ if (CurData <= DataRecord.MaxValue) {
+ DataRecord.MaxValue = CurData;
+ } else {
+ DataRecord.SampleState = SampleState_SearchMin;
+ DataRecord.MinValue = DataRecord.MaxValue;
+ }
+ break;
+ case FastCollapseLowSide:
+ case SlowCollapseHighSide:
+ if (CurData >= DataRecord.MaxValue) {
+ DataRecord.MaxValue = CurData;
+ } else {
+ DataRecord.SampleState = SampleState_SearchMin;
+ DataRecord.MinValue = DataRecord.MaxValue;
+ }
+ break;
+ }
+ break;
+ case SampleState_SearchMin:
+ switch (DataRecord.CollapseState & CollapseStateMask) {
+ case FastCollapseHighSide:
+ case SlowCollapseLowSide:
+ if (CurData > DataRecord.MinValue) {
+ DataRecord.MinValue = CurData;
+ }
+ DataRecord.SampleCnt++;
+ break;
+ case FastCollapseLowSide:
+ case SlowCollapseHighSide:
+ if (CurData < DataRecord.MinValue) {
+ DataRecord.MinValue = CurData;
+ }
+ DataRecord.SampleCnt++;
+ break;
+ }
+ break;
+ case SampleState_PreSampleBat:
+ if (SampleStateCnt > 4)
+ DataRecord.SampleState = SampleState_SampleBat;
+ ++SampleStateCnt;
+ break;
+ case SampleState_SampleBat:
+ DataRecord.BaseValue = CurData;
+ if (BatSampleWhileOn()) {
+ ADConv::SetChannel(ADConv::Ch_MotorCurrent,ADConv::Ref_MotorCurrent,ADConv::RightAdjust);
+ DataRecord.SampleState = SampleState_PreCurrentSample;
+ } else {
+ SetADChannelMotorNegative();
+ DataRecord.SampleState = SampleState_PreSearchMax;
+ }
+ break;
+ case SampleState_PreCurrentSample:
+ ADConv::SetChannel(ADConv::Ch_MotorCurrent,ADConv::Ref_MotorCurrent,ADConv::RightAdjust);
+ CurrentSampleCnt = 0;
+ DataRecord.SampleState = SampleState_CurrentSample1;
+ break;
+ case SampleState_CurrentSample1:
+ if (CurData > DataRecord.CurrentLimit) HandleOverload();
+ if (CurrentSampleCnt == 0) {
+ DataRecord.CurrentTemp = CurData;
+ DataRecord.CurrentMaxSearch = CurData;
+ }
+ ++CurrentSampleCnt;
+ if (CurData > DataRecord.CurrentMaxSearch) DataRecord.CurrentMaxSearch = CurData;
+ if (CurrentSampleCnt == 3) DataRecord.SampleState = SampleState_CurrentSample2;
+ break;
+ case SampleState_CurrentSample2:
+ if (CurData > DataRecord.CurrentLimit) HandleOverload();
+ // Sample delta-current. It is related to the back-EMF voltage, though
+ // this measurement is not precise enough to base control off of it
+ DataRecord.CurrentDelta = CurData - DataRecord.CurrentTemp;
+ DataRecord.CurrentTemp = CurData;
+ if (CurData > DataRecord.CurrentMaxSearch) DataRecord.CurrentMaxSearch = CurData;
+ DataRecord.SampleState = SampleState_CurrentSample3;
+ break;
+ case SampleState_CurrentSample3:
+ // We'll stay in this state until the on-phase ends...
+ if (CurData > DataRecord.CurrentLimit) HandleOverload();
+ DataRecord.CurrentTemp = CurData;
+ if (CurData > DataRecord.CurrentMaxSearch) DataRecord.CurrentMaxSearch = CurData;
+ break;
+ }
+ }
+}
+
+SIGNAL(SIG_OUTPUT_COMPARE1A) {HBridge::HandleIRQ(true);}
+SIGNAL(SIG_OUTPUT_COMPARE1B) {HBridge::HandleIRQ(false);}
+SIGNAL(SIG_ADC) {HBridge::Sample();}
+
+namespace TWI {
+ uint8_t Address;
+
+ extern const sConfigRecord ConfigRecord PROGMEM __attribute__ ((weak)) = {UniqueIDUnassigned,
+ DevClassHBridge,
+#if defined MEGA_BRIDGE
+ DevUmHBridge
+#elif defined H_BRIDGE
+ DevUmHBridge
+#elif defined SERVO_BRAIN
+ DevUmServoBrain
+#else
+#error No HW version is specified!
+#endif
+ };
+
+ enum UserStates {
+ US_ReceiveAddr = TWI::US_Base,
+ US_ReceiveData,
+ US_SendData
+ };
+
+ void HandleUserReceive() {
+ State = US_ReceiveAddr;
+ }
+
+ union {
+ uint8_t UInt8[4];
+ uint16_t UInt16[2];
+ uint32_t UInt32[1];
+ } TransmitBuffer;
+
+ void SendData() __attribute__ ((noinline));
+
+ void SendData() {
+ switch (HBridge::GetDataElementSize(Address)) {
+ default:
+ case 1:
+ TWIPrevData = *HBridge::GetDataRecord8(Address);
+ break;
+ case 2:
+ if ((Address & 1) == 0) {
+ TransmitBuffer.UInt16[0] = *HBridge::GetDataRecord16(Address);
+ }
+ TWIPrevData = TransmitBuffer.UInt8[Address & 1];
+ break;
+ case 4:
+ if ((Address & 3) == 0) {
+ TransmitBuffer.UInt32[0] = *HBridge::GetDataRecord32(Address);
+ }
+ TWIPrevData = TransmitBuffer.UInt8[Address & 3];
+ break;
+ }
+ TWDR = TWIPrevData;
+ ++Address;
+ if (Address >= HBridge::GetDataRecordSize()) Address = 0; // Wrap around
+ }
+
+ void GetData() {
+ switch (HBridge::GetDataElementSize(Address)) {
+ default:
+ case 1:
+ TransmitBuffer.UInt8[0] = TWIData;
+ *HBridge::GetDataRecord8(Address) = TransmitBuffer.UInt8[0];
+ break;
+ case 2:
+ TransmitBuffer.UInt8[Address & 1] = TWIData;
+ if ((Address & 1) != 0) {
+ // High-byte: store the whole word
+ *HBridge::GetDataRecord16(Address & (~0x01)) = TransmitBuffer.UInt16[0];
+ // Special-case request value, we have to save that in another spot as well...
+ if (Address == 1) {
+ HBridge::DataRecord.OriginalRequestValue = TransmitBuffer.UInt16[0];
+ }
+ }
+ break;
+ case 4:
+ TransmitBuffer.UInt8[Address & 3] = TWIData;
+ if ((Address & 3) != 0) {
+ // Highest byte: store the whole dword
+ *HBridge::GetDataRecord32(Address & (~0x03)) = TransmitBuffer.UInt32[0];
+ }
+ break;
+ }
+ ++Address;
+ if (Address >= HBridge::GetDataRecordSize()) Address = 0; // Wrap around
+ }
+
+ void HandleUserTransmit() {
+ SendData();
+ State = US_SendData;
+ }
+
+ void HandleUserState() {
+ switch (State) {
+ case US_ReceiveAddr:
+ switch (TWIStatus) {
+ case TW_SR_DATA_ACK:
+ // TODO: handle different command codes here -> check the data written to this address!
+ if (TWIData == 0xff) {
+ HBridge::SaveSettings();
+ ResetTWI();
+ } else {
+ //if ((TWIData & 0x7f) >= HBridge::GetDataRecordSize()) Address = HBridge::GetDataRecordSize() - 1; else Address = TWIData;
+ if (TWIData >= HBridge::GetDataRecordSize()) Address = HBridge::GetDataRecordSize() - 1; else Address = TWIData;
+ State = US_ReceiveData;
+ }
+ break;
+ default:
+ ResetTWI();
+ break;
+ }
+ break;
+ case US_ReceiveData:
+ switch (TWIStatus) {
+ case TW_SR_DATA_ACK:
+ GetData();
+ // We stay in this state for any optional additional data
+ break;
+ default:
+ ResetTWI();
+ break;
+ }
+ break;
+ case US_SendData:
+ switch (TWIStatus) {
+ case TW_ST_DATA_ACK:
+ SendData();
+ SETBIT(TWIControl,(1 << TWEA)); // require ACK
+ break;
+ default:
+ ResetTWI();
+ break;
+ }
+ break;
+ default:
+ // This really REALLY shouldn't happen...
+ ResetTWI();
+ break;
+ }
+ }
+}
+
+#ifdef USE_SERIAL_DEBUG
+// Debug interfaces
+namespace UsartComm {
+ inline void Init(uint16_t aBaudSetting) {
+ USART0::Init(aBaudSetting);
+ }
+ void HandleInput() {
+ uint8_t CurData = USART0::FastReceiveData();
+ OpLed::Toggle();
+ }
+}
+
+SIGNAL(SIG_USART_RECV) {UsartComm::HandleInput();}
+
+void DebugStat() {
+ cli();
+ int16_t VoltageSample = HBridge::DataRecord.VoltageSample;
+ uint16_t SampleCnt = HBridge::DataRecord.SampleCnt_Snapshot;
+ int16_t MinValue = HBridge::DataRecord.MinValue_Snapshot;
+ int16_t CurrentRequest = HBridge::DataRecord.CurrentRequest;
+ int16_t Error = HBridge::DataRecord.Error;
+ int16_t IValue = HBridge::DataRecord.IValue;
+ int16_t Command = HBridge::DataRecord.Command;
+ sei();
+
+ USART0::SendHexData(VoltageSample);
+ USART0::SendData(' '); USART0::SendData('S'); USART0::SendData(':'); USART0::SendHexData(SampleCnt);
+ USART0::SendData(' '); USART0::SendData('M'); USART0::SendData(':'); USART0::SendHexData(MinValue);
+ USART0::SendData(' '); USART0::SendData('R'); USART0::SendData(':'); USART0::SendHexData(CurrentRequest);
+ USART0::SendData(' '); USART0::SendData('E'); USART0::SendData(':'); USART0::SendHexData(Error);
+ USART0::SendData(' '); USART0::SendData('I'); USART0::SendData(':'); USART0::SendHexData(IValue);
+ USART0::SendData(' '); USART0::SendData('C'); USART0::SendData(':'); USART0::SendHexData(Command);
+ USART0::SendData('\r'); USART0::SendData('\n');
+}
+#endif // USE_SERIAL_DEBUG
+
+int main() {
+ cli();
+ EEPROM::Init();
+ OpLed::Init();
+ HBridge::BlinkCnt = 0;
+ #ifdef USE_SERIAL_DEBUG
+ UsartComm::Init(USART0::baud57600_8MHz);
+ #endif // USE_SERIAL_DEBUG
+ TCCR0A = 0;
+ TWI::Init();
+ ADConv::Init();
+ HBridge::Init();
+ SETBIT(DDRD,0x08|0x10);
+ sei();
+
+ // Everything happens in the interrupt routines. We have nothing else to do
+ // here but some optional debugging
+ while (true) {
+ #ifdef USE_SERIAL_DEBUG
+ cli();
+ if (HBridge::DataRecord.NewData) {
+ HBridge::DataRecord.NewData = false;
+ DebugStat();
+ } else {
+ sei();
+ }
+ #endif // USE_SERIAL_DEBUG
+ }
+ return 0;
+}
/Modules/HBRIDGE/HB2FET02A/SW/opled.h
0,0 → 1,34
#ifndef __opled_h__
#define __opled_h__
 
#include "common.h"
 
namespace OpLed {
#if defined BOARD_rs232if
const int OpLedMask=(0x01 << 4);
#elif defined BOARD_umServo32
const int OpLedMask=(0x01 << 4);
#elif defined BOARD_umHBridge
const int OpLedMask=(0x01 << 2);
#else
#error Unknown board specification
#endif
 
void inline Init() {
SETBIT(DDRD,OpLedMask);
}
 
void inline On() {
SETBIT(PORTD,OpLedMask);
}
 
void inline Off() {
CLRBIT(PORTD,OpLedMask);
}
 
void inline Toggle() {
TOGGLEBIT(PORTD,OpLedMask);
}
}
 
#endif // __opled_h__
/Modules/HBRIDGE/HB2FET02A/SW/twi_aap.cpp
0,0 → 1,375
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/sleep.h>
 
#include "common.h"
#include "usart.h"
#include "twi_aap.h"
#include "eeprom.h"
 
/*********
switch (TWOStatus) {
case TW_SR_SLA_ACK: //0x60
break;
case TW_SR_ARB_LOST_SLA_ACK: //0x68
break;
case TW_SR_GCALL_ACK: //0x70
break;
case TW_SR_ARB_LOST_GCALL_ACK: //0x78
break;
case TW_SR_DATA_ACK: //0x80
break;
case TW_SR_DATA_NACK: //0x88
break;
case TW_SR_GCALL_DATA_ACK: //0x90
break;
case TW_SR_GCALL_DATA_NACK: //0x98
break;
case TW_ST_LAST_DATA: //0xC8
case TW_SR_STOP: //0xA0
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on
break;
default: // All non-handled states will basically terminate any processing
State = MS_Idle;
CLRBIT(TWIControl,(1 << TWEA)); // will return NACK, i.e. will pretend to be unaddressed
break;
}
 
switch (TWOStatus) {
case TW_ST_SLA_ACK: //0xA8
break;
case TW_ST_ARB_LOST_SLA_ACK: //0xB0
break;
case TW_ST_DATA_ACK: //0xB8
break;
case TW_ST_DATA_NACK: //0xC0
break;
case TW_ST_ARB_LOST: //0x38 - let's hope it works
break;
case TW_ST_LAST_DATA: //0xC8
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on
break;
default: // All non-handled states will basically terminate any processing
State = MS_Idle;
CLRBIT(TWIControl,(1 << TWEA)); // will return NACK, i.e. will pretend to be unaddressed
break;
}
**********/
 
namespace TWI {
extern const sConfigRecord ConfigRecord PROGMEM __attribute__ ((weak)) = {UniqueIDUnassigned,DevClassUnassigned,DevUnassigned};
 
// void TWI::UserInit() __attribute__ ((weak));
void TWI::HandleUserReceive() __attribute__ ((weak));
void TWI::HandleUserTransmit() __attribute__ ((weak));
void TWI::HandleUserState() __attribute__ ((weak));
static inline void TWI::HandleState_MS_Idle();
static inline void TWI::HandleState_MS_Addressed();
static inline void TWI::StateMachine();
 
uint8_t ConfigCnt;
}
 
void TWI::ResetTWITransmit() {
// Same as above, but also returns the statemachine to the idle state
State = MS_Idle;
LastCmd = TWI_AAPCmd_Reserved;
// In slave-transmitter mode it's not possible to reset the TWI.
// But at least we can transmit all '1'-s so we won't interfere with the bus.
#ifdef SERIAL_DBG
USART0::SendData('*');
#endif
TWDR = 0xff;
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO));
}
void TWI::ResetTWIReceive() {
// Same as above, but also returns the statemachine to the idle state
State = MS_Idle;
LastCmd = TWI_AAPCmd_Reserved;
// In slave-transmitter mode it's not possible to reset the TWI.
// But at least we can transmit all '1'-s so we won't interfere with the bus.
#ifdef SERIAL_DBG
USART0::SendData('#');
#endif
TWDR = 0xff;
SETBIT(TWIControl,(1 << TWSTO));
CLRBIT(TWIControl,(1 << TWEA));
}
void TWI::ResetTWI() {
switch (TWIStatus) {
case TW_ST_LAST_DATA: //0xC8
case TW_SR_STOP:
case TW_ST_DATA_NACK:
case TW_SR_DATA_NACK:
case TW_SR_GCALL_DATA_NACK:
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on
CLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop aborting
break;
case TW_BUS_ERROR:
// This is the point where we re-enable the address recognition
ResetTWITransmit();
break;
default:
ResetTWIReceive();
break;
}
}
 
/*void TWI::UserInit() {
}*/
 
void TWI::HandleUserReceive() {
#ifdef SERIAL_DBG
USART0::SendData('r');
#endif
ResetTWI();
}
 
void TWI::HandleUserTransmit() {
#ifdef SERIAL_DBG
USART0::SendData('t');
#endif
ResetTWI();
}
 
void TWI::HandleUserState() {
}
 
 
static inline void TWI::HandleState_MS_Idle() {
switch (TWIStatus) {
case TW_SR_SLA_ACK:
LastCmd = TWI_AAPCmd_Reserved;
HandleUserReceive();
break;
case TW_SR_GCALL_ACK:
State = MS_Addressed;
break;
case TW_ST_SLA_ACK:
if ((TWIData & 0xfe) == GeneralCallAddr) {
// TWI_AAP read command: things depend on what the last (write) command was:
if (LastCmd == TWI_AAPCmd_GetConfig) {
// This is the read part of the GetUDID command: Send the byte count and change state
TWIPrevData = sizeof(ConfigRecord);
TWDR = TWIPrevData;
State = AAP_CmdGetConfig_SendConfig;
ConfigCnt = 0;
//SETBIT(TWIControl,(1 << TWEA)); // require ACK
} else {
//Unknown read command: ignore
ResetTWITransmit();
}
} else {
HandleUserTransmit();
}
break;
case TW_ST_LAST_DATA:
case TW_SR_STOP:
#ifdef SERIAL_DBG
USART0::SendData('.');
#endif
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on
CLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop aborting
break;
case TW_ST_DATA_ACK:
case TW_SR_DATA_ACK:
case TW_SR_GCALL_DATA_ACK:
ResetTWIReceive();
break;
case TW_BUS_ERROR:
case TW_ST_DATA_NACK:
case TW_SR_DATA_NACK:
case TW_SR_GCALL_DATA_NACK:
// This is the point where we re-enable the address recognition
ResetTWITransmit();
break;
default:
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWI
break;
}
}
 
static inline void TWI::HandleState_MS_Addressed() {
switch (TWIStatus) {
case TW_SR_GCALL_DATA_ACK:
switch(TWIData) {
case TWI_AAPCmd_ResetDevices:
LastCmd = TWIData;
State = MS_Idle;
SoftAddress = 0;
TWAR = SoftAddress | 1; // General call recognition and slave-receiver mode through address being 0
break;
case TWI_AAPCmd_ResetToPermAddress:
LastCmd = TWIData;
State = MS_Idle;
SoftAddress = EEPROM::GetByte(EEPROM_layout::TWI_SoftAddr_Ofs);
TWAR = SoftAddress | 1; // General call recognition and slave-receiver mode
if (SoftAddress != 0) {
// We have a valid address, do not respond to this command
LastCmd = TWI_AAPCmd_Reserved;
}
break;
case TWI_AAPCmd_GetConfig:
LastCmd = TWIData;
State = AAP_CmdGetConfig_GetAddress;
break;
case TWI_AAPCmd_AssignAddress:
LastCmd = TWIData;
ConfigCnt = 0;
State = AAP_CmdAssignAddress_GetUniqueID;
break;
default:
// unknown command - ignore the rest
ResetTWIReceive();
break;
}
break;
default:
ResetTWI();
break;
}
}
 
static inline void TWI::StateMachine() {
TWIStatus = TWSR & TW_STATUS_MASK;
TWIControl = TWCR;
if (TWIStatus != TW_SR_STOP) {
TWIData = TWDR;
} else {
// Fast short-circuit for stop-bit detection
// This condition cannot use clock-stretching so we might miss the next start if we don't hurry
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA)); // turn address recognition back on
CLRBIT(TWIControl,(1 << TWSTO)); // even if we did so, stop aborting
SETBIT(TWIControl,(1 << TWINT));
TWCR = TWIControl;
return;
}
 
#ifdef SERIAL_DBG
// DUMP state to serial port
USART0::SendData('s'); USART0::SendData(' '); USART0::SendHexData(TWIStatus); USART0::SendData(' ');
USART0::SendData('d'); USART0::SendData(' '); USART0::SendHexData(TWIData); USART0::SendData(' ');
USART0::SendData('S'); USART0::SendData(' '); USART0::SendHexData(State); USART0::SendData(' ');
USART0::SendData('L'); USART0::SendData(' '); USART0::SendHexData(LastCmd); USART0::SendData(' ');
USART0::SendData('A'); USART0::SendData(' '); USART0::SendHexData(SoftAddress); USART0::SendData('-');
#endif
switch (State) {
case MS_Idle:
HandleState_MS_Idle();
break;
case MS_Addressed:
HandleState_MS_Addressed();
break;
 
case AAP_CmdGetConfig_GetAddress:
switch (TWIStatus) {
case TW_SR_GCALL_DATA_ACK:
State = MS_Idle;
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWI
if (SoftAddress != TWIData) {
// We have a valid address, or was not addressed -> do not respond to this command
LastCmd = TWI_AAPCmd_Reserved;
}
break;
default:
ResetTWI();
break;
}
break;
 
case AAP_CmdAssignAddress_GetUniqueID:
switch (TWIStatus) {
case TW_SR_GCALL_DATA_ACK:
if (TWIData == pgm_read_byte(ConfigRecord.UniqueID+ConfigCnt)) {
++ConfigCnt;
if (ConfigCnt == UniqueID_Size) State = AAP_CmdAssignAddress_GetAddress;
} else {
// UDID is not ours, abort command
ResetTWIReceive();
}
break;
default:
ResetTWI();
break;
}
break;
case AAP_CmdAssignAddress_GetAddress:
switch (TWIStatus) {
case TW_SR_GCALL_DATA_ACK:
SoftAddress = TWIData & 0xfe;
if (TWIData & 0x01 == 1) {
EEPROM::SetByte(EEPROM_layout::TWI_SoftAddr_Ofs,SoftAddress);
}
State = MS_Idle;
TWAR = SoftAddress | 1; // General call recognition and normal operation
SETBIT(TWIControl,(1 << TWEA) | (1 << TWSTO)); // reset the TWI
LastCmd = TWI_AAPCmd_Reserved;
break;
default:
ResetTWI();
break;
}
break;
 
 
 
// Read-oriented commands
case AAP_CmdGetConfig_SendConfig:
switch (TWIStatus) {
case TW_ST_DATA_ACK:
if (TWIData != TWIPrevData) {
// We've lost the arbitration
ResetTWITransmit();
break;
}
TWIPrevData = pgm_read_byte(((uint8_t*)(&ConfigRecord))+ConfigCnt);
TWDR = TWIPrevData;
ConfigCnt++;
//At the last byte, we require NACK
if (ConfigCnt == sizeof(ConfigRecord)) {
State = MS_Idle;
CLRBIT(TWIControl,(1 << TWEA)); // require NACK
} else {
State = AAP_CmdGetConfig_SendConfig;
SETBIT(TWIControl,(1 << TWEA)); // require ACK
}
break;
 
default:
ResetTWI();
break;
}
break;
 
// User commands
default:
HandleUserState();
break;
}
// re-enable the TWI interrupts
SETBIT(TWIControl,1 << TWINT);
#ifdef SERIAL_DBG
USART0::SendData('S'); USART0::SendData(' '); USART0::SendHexData(State); USART0::SendData(' ');
USART0::SendData('L'); USART0::SendData(' '); USART0::SendHexData(LastCmd); USART0::SendData(' ');
USART0::SendData('A'); USART0::SendData(' '); USART0::SendHexData(SoftAddress); USART0::SendData(' ');
USART0::SendData('C'); USART0::SendData(' '); USART0::SendHexData(TWIControl); USART0::SendData(0x0d); USART0::SendData(0x0a);
#endif
TWCR = TWIControl;
}
 
SIGNAL(SIG_TWI) {TWI::StateMachine();}
 
uint8_t TWI::State;
uint8_t TWI::LastCmd;
uint8_t TWI::SoftAddress;
uint8_t TWI::TWIStatus;
uint8_t TWI::TWIData;
uint8_t TWI::TWIPrevData;
uint8_t TWI::TWIControl;
/Modules/HBRIDGE/HB2FET02A/SW/twi_aap.h
0,0 → 1,77
#ifndef __twi_aap_h__
#define __two_aap_h__
 
#include <compat/twi.h>
 
#include "common.h"
#include "usart.h"
#include "guids.h"
#include "eeprom.h"
//#define SERIAL_DBG
 
namespace EEPROM_layout {
const uint16_t TWI_SoftAddr_Ofs = 0xff;
}
 
namespace TWI {
const uint8_t GeneralCallAddr = 0;
const size_t UniqueID_Size = 16*3;
const size_t GUID_Size = sizeof(GUID);
 
struct sConfigRecord {
uint8_t UniqueID[UniqueID_Size];
GUID ClassID;
GUID DevID;
};
 
enum TWI_AAPCommands {
TWI_AAPCmd_Reserved = 0,
TWI_AAPCmd_Start = 0x20,
TWI_AAPCmd_End = 0x21,
TWI_AAPCmd_ResetDevices = 0x22,
TWI_AAPCmd_ResetToPermAddress = 0x23,
TWI_AAPCmd_GetConfig = 0x24,
TWI_AAPCmd_AssignAddress = 0x25
};
enum MainStates {
MS_Idle = 0,
MS_Addressed,
AAP_CmdGetConfig_GetAddress,
AAP_CmdGetConfig_SendConfig,
AAP_CmdAssignAddress_GetUniqueID,
AAP_CmdAssignAddress_GetAddress,
US_Base
};
 
extern uint8_t State;
extern uint8_t LastCmd;
extern uint8_t SoftAddress;
 
inline void Init() {
TWBR = 32; // 100kHz with an 8MHz crystal
// TWBR = 16; // 200kHz with an 8MHz crystal
TWSR = 0; // pre-scaler: 1
TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE) | (1 << TWINT);
if (TWSR == TW_BUS_ERROR) SETBIT(TWCR,(1 << TWSTO)); // reset TWI
TWAMR = 0; // all address bits matter
State = MS_Idle;
SoftAddress = EEPROM::GetByte(EEPROM_layout::TWI_SoftAddr_Ofs);
if (SoftAddress == 0xff) SoftAddress = 0;
SoftAddress &= 0xfe;
// TWAR = GeneralCallAddr | 1; // General call recognition and slave-receiver mode through address being 0
TWAR = SoftAddress | 1;
LastCmd = TWI_AAPCmd_Reserved;
}
 
// These used in all states, including user states, so make them available.
extern uint8_t TWIStatus;
extern uint8_t TWIData;
extern uint8_t TWIPrevData;
extern uint8_t TWIControl;
 
void ResetTWITransmit();
void ResetTWIReceive();
void ResetTWI();
}
 
#endif // __twi_aap_h__
/Modules/HBRIDGE/HB2FET02A/SW/usart.cpp
0,0 → 1,65
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
 
#include "common.h"
#include "usart.h"
 
 
void USART0::SendData(uint8_t aData) {
while(!TESTBIT(UCSR0A,(1 << UDRE0)));
UDR0 = aData;
}
 
uint8_t USART0::ReceiveData() {
while(!TESTBIT(UCSR0A,(1 << RXC0)));
return UDR0;
}
 
// a non-exported helper function
static char MakeHexDigit(uint8_t aDigit) {
if (aDigit > 9) return 'a'+aDigit-10; else return '0'+aDigit;
}
 
void USART0::SendHexData(uint8_t aData) {
SendData(MakeHexDigit(aData >> 4));
SendData(MakeHexDigit(aData & 0x0f));
}
 
void USART0::SendHexData(uint16_t aData) {
SendHexData((uint8_t)(aData >> 8));
SendHexData((uint8_t)(aData & 0xff));
}
 
void USART0::SendHexData(uint32_t aData) {
SendHexData((uint16_t)(aData >> 16));
SendHexData((uint16_t)(aData & 0xffff));
}
 
void USART0::SendHexData(int16_t aData) {
if (aData < 0) {
SendData('-');
aData = -aData;
} else {
SendData(' ');
}
SendHexData((uint16_t)aData);
}
 
void USART0::SendHexData(int32_t aData) {
if (aData < 0) {
SendData('-');
aData = -aData;
} else {
SendData(' ');
}
SendHexData((uint32_t)aData);
}
 
void USART0::SendString(const char *aString) {
while(aString != 0) SendData(*(aString++));
}
 
void USART0::SendPGString(const char *aString) {
while(aString != 0) SendData(pgm_read_byte(aString++));
}
/Modules/HBRIDGE/HB2FET02A/SW/usart.h
0,0 → 1,41
#ifndef __usart_h__
#define __usart_h__
 
#include "common.h"
 
namespace USART0 {
// setting = f_osc/16/BAUD - 1
const uint16_t baud9600_8MHz = 51;
const uint16_t baud19200_8MHz = 25;
const uint16_t baud57600_8MHz = 8;
const uint16_t baud19200_6_14MHz = 19;
const uint16_t baud38400_6_14MHz = 9;
const uint16_t baud19200_6_5MHz = 20;
const uint16_t baud38400_6_5MHz = 10;
const uint16_t baud57600_6_5MHz = 6;
inline uint8_t FastReceiveData();
inline void Init(uint16_t aBaudSetting) {
UBRR0 = aBaudSetting;
UCSR0A = 0;
FastReceiveData(); // Clear any possible pending input data (to avoid spurious IRQ
UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (1 << USBS0) | (3 << UCSZ00);
}
void SendData(uint8_t aData);
uint8_t ReceiveData();
inline void FastSendData(uint8_t aData) {
UDR0 = aData;
}
inline uint8_t FastReceiveData() {
return UDR0;
}
void SendHexData(uint8_t aData);
void SendHexData(uint16_t aData);
void SendHexData(uint32_t aData);
void SendHexData(int16_t aData);
void SendHexData(int32_t aData);
void SendString(const char *aString);
void SendPGString(const char *aString);
}
 
#endif // __usart_h__