From c74357658d036413d75f9beadcc816744d19525a Mon Sep 17 00:00:00 2001 From: crashkopf Date: Mon, 13 Jan 2014 11:48:29 -0800 Subject: [PATCH 1/7] libAPLUI and minor fixes This is the initial release of the APL UI library. Also, there are some minor fixes to KSlibs: Added the Disp_Clear() function for clearing the LCD display Fixed a bug where keys aren't read after the display is cleared --- .DS_Store | Bin 12292 -> 0 bytes KSlibs/display.c | 39 +++++++++----- KSlibs/display.h | 1 + KSlibs/keypad.c | 22 ++++---- libAPLUI/README.txt | 46 ++++++++++++++++ libAPLUI/dialog.c | 54 +++++++++++++++++++ libAPLUI/dialog.h | 23 ++++++++ libAPLUI/menu.c | 107 ++++++++++++++++++++++++++++++++++++ libAPLUI/menu.h | 40 ++++++++++++++ libAPLUI/tree.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ libAPLUI/tree.h | 57 ++++++++++++++++++++ libAPLUI/view.c | 74 +++++++++++++++++++++++++ libAPLUI/view.h | 82 ++++++++++++++++++++++++++++ libAPLUI/viewmux.c | 51 ++++++++++++++++++ 14 files changed, 698 insertions(+), 27 deletions(-) delete mode 100644 .DS_Store create mode 100644 libAPLUI/README.txt create mode 100644 libAPLUI/dialog.c create mode 100644 libAPLUI/dialog.h create mode 100644 libAPLUI/menu.c create mode 100644 libAPLUI/menu.h create mode 100644 libAPLUI/tree.c create mode 100644 libAPLUI/tree.h create mode 100644 libAPLUI/view.c create mode 100644 libAPLUI/view.h create mode 100644 libAPLUI/viewmux.c diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index cdb66725d8edfe025ec0953eef887ab35a37240e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12292 zcmeI1&u<$=6vy9l(qvao+ex8KQmSHz3KbaY81k!%ij%liLZ})w4i0I+ti97@#d-jJSgryzjwhLEPx9RnMAQJ){CxRq=2uA>#ie$RO%Oub#xx;>NcqKtmGZf z_UluA)w9L5g*Csv;MlxqRbCAHffZE6lvN2W^H#7%maw0ruLaa!iM;cU9jp~tf1{Be z9!(~OQt8cPqA@l`{o(QS=B7!7eQz9p_{oWxob~GFYp*v|S{OYV;IJBRx3{RbBbD}V z6b6)mQ?;p;09M*hf*WyWMXu9Y&X+}geq^C9;5p)y-CKqP^_FQuwyd*%6j1Vo_L;D1gU8nza2hul=U9F@BZT_ z9(?5S)bROdvP&huT;^UNa-LP@*KE4fbcIuE=LD^^=vqbY=3b#Kov*2NUXnh~St7Wy z&OL6g$l+z*cCI_z-sp3c`C;S^oISeM)Q1xu8;XFJ~NA}z!>j9yHdta~f* z(7;J^&^)u#+YGIs);b%1%wXRbEEW#UC*tQ!W*MwQS<~jz2D43eNLfeCagzy?bt>zG zdBI?FChJne$@sLvelnP$oG-;^Ot~@+X3nY~doYty9r`0=0FrP4uEH{uXcyPvZFmRX zh4ckl!J2tUIX{0@J>pNQCvz1WBS_yC^5)A%T!!(kl3aXgO`co8q*49?;l z=5P`7xP;4CLL0B+Dmqv}fmN*G2HwP5_y)d-Z{hp+0e*-d;m7z1eu}s8GyELysC$dG zw&GlLuaV%lJI8vY3EUfj4&~M_&;J+q|Neh(i)k-SKoj^^62Q*vLUxL7z4eCKorOGW zC#b$mRTk;jR2qg*BVFWtcpN9){)Zv;6Ok #define DelayMS(n) delay(n) +/* +Real important stuff: + Pins 0..2 of PORTG are Strobe, R/W, and Reg Select + Pins 0..3 of PORTA are D0..D3 +*/ #define DISP_IN {PORTA |= 0x0f; DDRA &= ~0x0f;} -#define DISP_OUT {DDRA |= 0x0f; } +#define DISP_OUT {DDRA |= 0x0f;} #define DISP_EN_HIGH {PORTG |= 0x01;} #define DISP_EN_LOW {PORTG &= ~0x01;} #define DISP_READ {PORTG |= 0x02;} @@ -31,18 +36,18 @@ void Disp_Init() { unsigned char Disp_ReadSR() { unsigned char highnib, lownib; - DISP_IN; - PORTG &= ~7; - DISP_IR; - DISP_READ; + DISP_IN; // {PORTA |= 0x0f; DDRA &= ~0x0f;} Make PORTA input, turn on pull-up resistors + PORTG &= ~7; // Turn off RW and RS + DISP_IR; // {PORTG &= ~0x04;} Turn off RS? + DISP_READ; // {PORTG |= 0x02;} Turn on RW - DISP_EN_HIGH; // read IR en - highnib = DISP_DATA_IN; - DISP_EN_LOW; + DISP_EN_HIGH; // read IR en // {PORTG &= ~0x01;} Strobe on + highnib = DISP_DATA_IN; // (PINA & 0x0f) Read nibble + DISP_EN_LOW; // {PORTG &= ~0x01;} Strobe off - DISP_EN_HIGH; - lownib = DISP_DATA_IN; - DISP_EN_LOW; + DISP_EN_HIGH; // {PORTG &= ~0x01;} Strobe on + lownib = DISP_DATA_IN; // (PINA & 0x0f) Read nibble + DISP_EN_LOW; // {PORTG &= ~0x01;} Strobe off return (highnib << 4) + lownib; } @@ -152,9 +157,9 @@ void Disp_Reset() { void Disp_Wait() { -// while (Disp_ReadSR() & 0x01) ; -//delay(1); -delayMicroseconds(75); +// while (Disp_ReadSR() & 0x40); +// delay(1); + delayMicroseconds(75); } @@ -209,3 +214,9 @@ void Disp_CursOff() { Disp_WriteIR(0x0c); // display on, cursor off Disp_Wait(); } + +void Disp_Clear() { + Disp_WriteIR(0x01); // display clear + //Disp_Wait(); + DelayMS(2); +} diff --git a/KSlibs/display.h b/KSlibs/display.h index eb1cce1..9ccf871 100755 --- a/KSlibs/display.h +++ b/KSlibs/display.h @@ -15,6 +15,7 @@ void Disp_PutChar(char); void Disp_PutStr(char*); void Disp_CursOn(); void Disp_CursOff(); +void Disp_Clear(); diff --git a/KSlibs/keypad.c b/KSlibs/keypad.c index 5f34bee..89fa930 100755 --- a/KSlibs/keypad.c +++ b/KSlibs/keypad.c @@ -1,31 +1,28 @@ #include - - void Kpd_Init() { - PORTA |= 0xf0; - DDRA &= ~0xf0; // key0 - key3 are inputs with pullup + Kpd_Reset(); } - - void Kpd_Reset() { + PORTA = 0xf0; // Makse sure all key pins are set for input + DDRA = 0x0f; // key0 - key3 are inputs with pullup + } - -#define PBIT(n) (1 << (n)) -#define NBIT(n) (0x0f ^ PBIT(n)) +#define PBIT(n) (1 << (n)) // Set a single bit +#define NBIT(n) (0x0f ^ PBIT(n)) // Clear a single bit, but keep the rest set (low nibble) #define SCAN(n) {PORTA |= NBIT(n); PORTA &= ~PBIT(n); DDRA &= ~NBIT(n); DDRA |= PBIT(n); } -#define NOSCAN { PORTA |= 0x0f; DDRA &= ~0x0f; } +#define NOSCAN { PORTA |= 0x0f; DDRA &= ~0x0f; } // Set all bits in lower nibble; clear all bit in lower nibble of DDR int Kpd_Scan() { unsigned char i, row; - + Kpd_Reset(); // This fixes a bug where keys aren't read after the display is cleared for (i=0; i<4; i++) { SCAN(i); - row = ~((PINA & 0xf0) >> 4); + row = ~((PINA & 0xf0) >> 4); // Read key bits from upper nibble and shift to lower nibble, then invert if (row & 0x01) return i << 2; if (row & 0x02) return (i << 2) + 1; if (row & 0x04) return (i << 2) + 2; @@ -42,7 +39,6 @@ int Kpd_Scan() { #undef NOSCAN - int Kpd_GetKeyAsync() { static int keylast = -1; diff --git a/libAPLUI/README.txt b/libAPLUI/README.txt new file mode 100644 index 0000000..8db893e --- /dev/null +++ b/libAPLUI/README.txt @@ -0,0 +1,46 @@ +Copyright 2014 All Power Labs, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=== All Power Labs User Interface Library v0.1 === + +This library is designed to bring a consistant user interface system to the +Power Pallet PCU. It provides functions and structures for basic views such +as menus and dialogs. There is also a multiplexer system for switching views. +The whole thing is written in C, but probly be re-written in C++ we can update +the code to support later versions of AVR libc that don't leak memory. + +view.h - Basic view structure. Views have callback functions for key handling +and updating the display. There are also entrance and exit hooks. The view +multiplexer handles loading, unloading, passing key events, and calling the +update function of the current view. + +tree.h - a linked list tree structure and associated functions + +menu.h - Menu is a sub-class of view that navigates a tree structure and +allows selection of an item from the tree. + +dialog.h - Dialog is a sub-class of view that asks a question, usualy accepting +a yes or no response. + +More documentation to follow... later. \ No newline at end of file diff --git a/libAPLUI/dialog.c b/libAPLUI/dialog.c new file mode 100644 index 0000000..7e39f42 --- /dev/null +++ b/libAPLUI/dialog.c @@ -0,0 +1,54 @@ +#include +#include "view.h" +#include "dialog.h" + +void Dialog_entry (dialog_ptr me) { +} +void Dialog_exit (dialog_ptr me) { +} +void Dialog_update (dialog_ptr me, viewport_ptr port) { + +} + /* + void Dialog_Adj_update (const view_ptr v) { + dialog_adj_t * i = (dialog_adj_t *) v->env; + if (v->shouldUpdate) { + Disp_Clear(); + Disp_RC(0,0); fprintf_P(v->dev, i->label); + Disp_RC(1,0); + switch (CfgGetType(i->value)) { + case CFGTYPE_BYTE: + fprintf_P(v->dev, PSTR("%u"), (byte) *CfgGetVal(i->value)); + break; + case CFGTYPE_INT: + fprintf_P(v->dev, PSTR("%i"), (int) *CfgGetVal(i->value)); + break; + case CFGTYPE_UINT: + fprintf_P(v->dev, PSTR("%u"), (unsigned int) *CfgGetVal(i->value)); + break; + case CFGTYPE_LONG: + fprintf_P(v->dev, PSTR("%li"), (long) *CfgGetVal(i->value)); + break; + case CFGTYPE_ULONG: + fprintf_P(v->dev, PSTR("%lu"), (unsigned long) *CfgGetVal(i->value)); + break; + default: + break; + } + Disp_RC(2,0); + fprintf_P( + v->dev, + PSTR("%S %lu %lu %lu"), + CfgGetTypeStr(CfgGetType(i->value)), + CfgGetMin(i->value), + CfgGetMax(i->value), + CfgGetDef(i->value) + ); + Disp_RC(3,0); fprintf_P(v->dev, PSTR("EXIT")); + //Disp_RC(3,5); fprintf_P(v->dev, PSTR("ENTER")); + Disp_RC(3,12); fprintf_P(v->dev, PSTR("+")); + Disp_RC(3,17); fprintf_P(v->dev, PSTR("-")); + v->shouldUpdate = 0; + } +} +*/ diff --git a/libAPLUI/dialog.h b/libAPLUI/dialog.h new file mode 100644 index 0000000..ce2a2e5 --- /dev/null +++ b/libAPLUI/dialog.h @@ -0,0 +1,23 @@ +typedef struct dialog * dialog_ptr; + +typedef struct keylabel { + +} keylabel_t; + +typedef struct dialog { + struct view; + const char * message; +} dialog_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +void Dialog_entry (const dialog_ptr); +void Dialog_exit (const dialog_ptr); +void Dialog_update (const dialog_ptr, viewport_ptr port); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libAPLUI/menu.c b/libAPLUI/menu.c new file mode 100644 index 0000000..6888675 --- /dev/null +++ b/libAPLUI/menu.c @@ -0,0 +1,107 @@ +#include +#include +#include "view.h" +#include "tree.h" +#include "menu.h" + +void key0(menu_ptr m, char key); +void key1(menu_ptr m, char key); +void key2(menu_ptr m, char key); +void key3(menu_ptr m, char key); + +keyaction_t key0_act = {0, key0}; +keyaction_t key1_act = {1, key1}; +keyaction_t key2_act = {2, key2}; +keyaction_t key3_act = {3, key3}; + +keyaction_t * menu_actions[] = { + &key0_act, + &key1_act, + &key2_act, + &key3_act, +}; + +void MenuConstruct (menu_ptr menu, menunode_p node, selector_f sfunc) { + ViewConstruct(&menu->view, (update_f) Menu_update); + menu->view.actions = &menu_actions; // I don't think this is the best way to do this... + menu->select = sfunc; + menu->node = node; + menu->top = MenuGetFirst(menu); + menu->cursor = MenuGetFirst(menu); +} +void MenunodeConstruct (menunode_p item, const char * label, viewfactory_f factory) { + item->label = label; + item->factory = factory; +} +menunode_p MenuGetFirst (menu_ptr menu) { + return GetChild((node_p) menu->node); +} +menunode_p MenuGetLast (menu_ptr menu) { + menunode_p x = MenuGetFirst(menu); + while (NextNode((node_p) x)) x = NextNode((node_p) x); + return x; +} +menunode_p MenuGetParent (menu_ptr menu) { + return GetParent ((node_p) menu->node); +} + +void MenuAppend (menu_ptr menu, menunode_p node) { + BranchAppend((node_p) MenuGetFirst(menu), (node_p)node); +} +void Menu_update (menu_ptr menu, viewport_ptr port) { + int x; + menunode_p i; + fputc('\f', &port->dev); + if (menu->top) { + // Adjust top so that the cursor is within view + if(IsAhead(menu->top, menu->cursor) > (port->h - 1)) + menu->top = NodeSeek(menu->cursor, -(port->h - 1)); + if(IsBehind(menu->top, menu->cursor)) + menu->top = menu->cursor; + i = menu->top; // Start at the top + for (x=0; x < (port->h); x++) { + if (i) { + fprintf_P(&port->dev, PSTR("% 2i "), IsBehind(i, MenuGetFirst(menu)) + 1); + fprintf_P(&port->dev, i->label); + } + if (i == menu->cursor) + fprintf_P(&port->dev, PSTR(" <-")); + i = (menunode_p) NextNode(i); // Advance to next item + fputc('\n', &port->dev); + } + } + else { + fprintf_P(&port->dev, PSTR("Nothing Here...\n\n\n")); + } + // if (MenuGetParent(menu)) + // fprintf_P(&port->dev, PSTR("EXIT ")); + // else + // fprintf_P(&port->dev, PSTR(" ")); + // if (menu->cursor->factory || GetChild((node_p) menu->cursor)) + // fprintf_P(&port->dev, PSTR("ENTER")); + // else + // fprintf_P(&port->dev, PSTR(" ")); + // if (menu->cursor != MenuGetFirst(menu)) + // fprintf_P(&port->dev, PSTR(" UP ")); + // else + // fprintf_P(&port->dev, PSTR(" ")); + // if (menu->cursor != MenuGetLast(menu)) + // fprintf_P(&port->dev, PSTR(" DOWN")); + // else + // fprintf_P(&port->dev, PSTR(" ")); +} + +void key0(menu_ptr menu, char key) { + menu->view.eject(menu); +} +void key1(menu_ptr menu, char key) { + if (menu->select) menu->select(menu, menu->cursor); +} +void key2(menu_ptr menu, char key) { + if (menu->cursor && PrevNode(menu->cursor)) + menu->cursor = (menunode_p) PrevNode(menu->cursor); +} +void key3(menu_ptr menu, char key) { + if (menu->cursor && NextNode(menu->cursor)) + menu->cursor = (menunode_p) NextNode(menu->cursor); +} \ No newline at end of file diff --git a/libAPLUI/menu.h b/libAPLUI/menu.h new file mode 100644 index 0000000..60be496 --- /dev/null +++ b/libAPLUI/menu.h @@ -0,0 +1,40 @@ +typedef struct menunode * menunode_p; +typedef struct menu * menu_ptr; + +typedef void (* selector_f)(menu_ptr menu, menunode_p node); +typedef void (* menuexit_f)(menu_ptr menu); + +typedef struct menunode { + node_s node; + viewfactory_f factory; + const char * label; +} menunode_s; + +typedef struct menu { + view_t view; + selector_f select; + menunode_p node; + menunode_p root; + menunode_p top; + menunode_p cursor; +} menu_t; + +//extern keyaction_t * menu_actions[]; + +#ifdef __cplusplus +extern "C" { +#endif + +void MenuConstruct (menu_ptr menu, menunode_p root, selector_f sfunc); +void MenunodeConstruct (menunode_p node, const char * label, viewfactory_f factory); + +menunode_p MenuGetFirst (menu_ptr menu); +menunode_p MenuGetLast (menu_ptr menu); +menunode_p MenuGetParent (menu_ptr menu); +void MenuAppend (menu_ptr menu, menunode_p node); + +void Menu_update (menu_ptr, viewport_ptr); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libAPLUI/tree.c b/libAPLUI/tree.c new file mode 100644 index 0000000..5539301 --- /dev/null +++ b/libAPLUI/tree.c @@ -0,0 +1,129 @@ +#include "tree.h" + +void NodeConstruct(node_p node) { + node->parent = 0; + node->children = 0; + node->prev = 0; + node->next = 0; +} + +// Insert a node to the list of siblings +void NodeInsert (node_p dst, node_p src) { + src->prev = dst; + src->next = dst->next; + dst->next = src; + src->next->prev = src; +} + +// Append a node to the end of a branch +void BranchAppend (node_p branch, node_p node) { + if (branch->parent) + node->parent = branch->parent; + if (node->prev = Walk(branch, IsTerminus, 0)) + node->prev->next = node; +} + +// Remove a node from a branch +void BranchRemove (node_p parent, node_p child) {} + +// Append a node to the children of another node +void NodeAppend(node_p parent, node_p child){ + if (parent->children) + BranchAppend(parent->children, child); + else + parent->children = child; + child->parent = parent; +} +void NodeRemove(node_p parent, node_p child){} + +// Compare identity +int NodeCompare(node_p n1, node_p n2) { + return (n1 == n2); +} + +int IsLeaf(node_p node) { // True if this is the only node in the branch + return !( + (int) node->next || + (int) node->prev || + (int) node->children + ); +} +int IsBranch(node_p node) { // True if there's other nodes attached to this one + return ( + node && ( + (int) node->next || + (int) node->prev || + (int) node->children + ) + ); +} +int IsTerminus(node_p node) { // True if there's no next node on the branch + return !( + (int) node && + (int) node->next + ); +} +int IsParent(node_p node) { + return (int) node->children; +} +int IsAhead(node_p n1, node_p n2){ + node_p x = n1; + int i = 0; + while (!NodeCompare(n2, x) && x) { + x = NextNode(x); + i++; + } + if (!x) i = 0; + return i; +} +int IsBehind(node_p n1, node_p n2){ + node_p x = n1; + int i = 0; + while (!NodeCompare(n2, x) && x) { + x = PrevNode(x); + i++; + } + if (!x) i = 0; + return i; +} + +int NextNode(node_p node){ + if (node) return node->next; + else return 0; +} +int PrevNode(node_p node){ + if (node) return node->prev; + else return 0; +} +node_p GetParent(node_p node) { + return node->parent; +} +node_p GetChild(node_p parent) { + return parent->children; +} +node_p NodeSeek(node_p start, int dist){ + node_p x = start; + int i = 0; + while (x && i != dist) { + if (dist > 0) { + if (x->next) x = x->next; + i++; + } + else { + if (x->prev) x = x->prev; + i--; + } + } + return x; +} +// Walk the tree looking for a matching node +node_p Walk (node_p node, node_f testfunc, unsigned int depth) { + node_p x; + if (!node) return 0; // NULL node gets none! + if (testfunc(node)) return node; // This node is the one we want. + if (depth > 0) // Make sure we don't go too deep + if(x = Walk(node->children, testfunc, depth - 1)) // Walk down + return x; + if (x = Walk(node->next, testfunc, depth)) // Walk across + return x; +} \ No newline at end of file diff --git a/libAPLUI/tree.h b/libAPLUI/tree.h new file mode 100644 index 0000000..9a3142b --- /dev/null +++ b/libAPLUI/tree.h @@ -0,0 +1,57 @@ +/* + Node - any single point in the tree + Branch - a node, or lateral set of nodes, with one or more descendants + Twig - a lateral set of nodes with no descendants + Root - a node with no parent, and the first node in a branch + Leaf - a node with no descendants and no lateral siblings + Terminus - the last node in a branch +*/ + +typedef struct node * node_p; + +typedef int (* node_f)(node_p); // Tests a node for some condition. Returns non-zero for success. + +typedef struct node { + node_p parent; // Parent node. NULL if this node is the root. + node_p children; // Child node. NULL if this node is a leaf. + node_p prev; // Previous lateral node. NULL if this node is first. + node_p next; // Next lateral node. NULL if this node is last. +} node_s; + +#ifdef __cplusplus +extern "C" { +#endif + +void NodeConstruct(node_p node); +/* +Walk the tree looking for the first node that matches. Traversal is depth +first, then lateral. If depth is reached without finding a match, no further +descent is made. +*/ +node_p Walk (node_p root, node_f, unsigned int depth); + +int IsBranch(node_p); +int IsLeaf(node_p); +int IsTerminus(node_p); +int IsParent(node_p); + +int IsAhead(node_p n1, node_p n2); +int IsBehind(node_p n1, node_p n2); + +int NextNode(node_p node); +int PrevNode(node_p node); + +node_p GetParent(node_p node); +node_p GetChild(node_p parent); +node_p NodeSeek(node_p start, int dist); + +void NodeInsert (node_p n1, node_p n2); // Insert a node into a branch +void BranchAppend (node_p branch, node_p node); // Append a node to the end of a branch +void BranchRemove (node_p branch, node_p node); // Remove a node from the branch +void NodeAppend(node_p parent, node_p child); +void NodeRemove(node_p parent, node_p child); +int NodeCompare (node_p n1, node_p n2); // Compare two nodes for equal identity + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libAPLUI/view.c b/libAPLUI/view.c new file mode 100644 index 0000000..e6c027e --- /dev/null +++ b/libAPLUI/view.c @@ -0,0 +1,74 @@ +/* + + + + dialog - A sub-class of view. It represents question posed to the user. + The response to that question will trigger some sort of control event. + + menu - A sub-class of view. It presents a list of options to be selected from. +*/ + + +#include +#include +#include "view.h" + +// Default handlers. These aren't public, so we declare them here instead of the header. +void ViewDefaultEjectFunc(view_ptr view); +void ViewDefaultKeyFunc(view_ptr view, char key); + +void ViewConstruct(view_ptr view, update_f update) { + view->mux = 0; + view->entry = 0; + view->exit = 0; + view->update = update; + view->key = ViewDefaultKeyFunc; + view->actions = 0; + view->eject = ViewDefaultEjectFunc; + view->priv = 0; + view->shouldUpdate = 0; +} + +int ViewGetUpdate(view_ptr view) { + if (view) return view->shouldUpdate; + else return 0; +} +void ViewSetUpdate(view_ptr view) { + if (view) view->shouldUpdate++; +} +void ViewClearUpdate(view_ptr view) { + if (view) view->shouldUpdate = 0; +} + +keyaction_ptr ViewResolveAction(view_ptr view, char key) { + // Find the first action that matches and return it. + int x = 0; + while (view->actions[x] != 0) { + if (view->actions[x]->key == key) { + return view->actions[x]; + } + x++; + } + return 0; +} + +keyhand_f ViewResolveKeyFunc(view_ptr view, char key) { + keyaction_ptr a = ViewResolveAction(view, key); + if (a) return a->func; + else return 0; +} + +void ViewDefaultKeyFunc(view_ptr me, char key) { + keyhand_f keyfunc; + keyfunc = ViewResolveKeyFunc(me, key); + if(keyfunc) { + keyfunc(me, key); + ViewSetUpdate(me); + } +} + +void ViewDefaultEjectFunc(view_ptr me) { + if (!me->mux) return; + if (!me->mux->eject) return; + me->mux->eject(me->mux, me); +} \ No newline at end of file diff --git a/libAPLUI/view.h b/libAPLUI/view.h new file mode 100644 index 0000000..3619554 --- /dev/null +++ b/libAPLUI/view.h @@ -0,0 +1,82 @@ +/* + viewport - An abstraction of an input and output device. It polls for user input events, and displays program outputs. + Input events are passed on to the attached view. + + view - A view is the presentation layer, interacting through a viewport. It is the generic case of higher-level + UI constructs. It reacts to inputs and generates outputs. When a view internally decides to end its control + of the display, it calls is eject() function. By default this passes the eject event on to the multiplexer. + + enter function - Called on entry into the view + exit function - Called when exiting the view + update function - Called periodically to display any updates + + viewmux - Multiplexes views onto the display. + +*/ + +typedef struct view * view_ptr; +typedef struct viewmux * viewmux_ptr; +typedef struct viewport * viewport_ptr; +typedef struct keyaction * keyaction_ptr; + +typedef view_ptr (* viewfactory_f)(); + +typedef void (* view_f)(view_ptr); +typedef void (* update_f)(view_ptr, viewport_ptr); +typedef void (* keyhand_f)(view_ptr, char); +typedef void (* vmuxeject_f)(viewmux_ptr, view_ptr); +typedef view_ptr (* vmuxaccept_f)(viewmux_ptr); + +typedef struct viewport { + FILE dev; + unsigned int w; + unsigned int h; + unsigned int row; + unsigned int col; +} viewport_t; + +typedef struct keyaction { + char key; + keyhand_f func; +} keyaction_t; + +typedef struct view { + viewmux_ptr mux; // The multiplexer handling this view + view_f entry; // Entry hook, for interacting with control + view_f exit; // Exit hook, for interacting with control + update_f update; // Update hook, for drawing to the viewport + keyhand_f key; // Handler function that all keys pass through + keyaction_ptr * actions; // List of key action hooks + view_f eject; // Call this when we're done with the current view + void * priv; // Anonymous private data for callback functions + unsigned int shouldUpdate; // Flag to notify us it's time to update +} view_t; + +typedef struct viewmux { + viewport_ptr port; + view_ptr current; + vmuxaccept_f accept; + vmuxeject_f eject; +} viewmux_t; + +#ifdef __cplusplus +extern "C" { +#endif +void ViewConstruct(view_ptr view, update_f update); + +keyhand_f ViewResolveKeyFunc(view_ptr view, char key); +keyaction_ptr ViewResolveAction(view_ptr view, char key); + +int ViewGetUpdate(view_ptr view); +void ViewSetUpdate(view_ptr view); +void ViewClearUpdate(view_ptr view); + +void VmuxConstruct(viewmux_ptr mux, viewport_ptr port); +void VmuxTick(viewmux_ptr mux); +void VmuxSwitch(viewmux_ptr mux, view_ptr view); + +void VmuxDefaultEjectFunc(viewmux_ptr me, view_ptr view); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libAPLUI/viewmux.c b/libAPLUI/viewmux.c new file mode 100644 index 0000000..01f51f1 --- /dev/null +++ b/libAPLUI/viewmux.c @@ -0,0 +1,51 @@ +#include +#include "view.h" + +view_ptr VmuxDefaultAcceptFunc(viewmux_ptr me); +void VmuxDefaultEjectFunc(viewmux_ptr me, view_ptr view); + +void VmuxConstruct(viewmux_ptr mux, viewport_ptr port) { + mux->port = port; + mux->current = 0; + mux->accept = VmuxDefaultAcceptFunc; + mux->eject = VmuxDefaultEjectFunc; +} + +// Eject the current view and install a new one. It's okay for view to be NULL +void VmuxSwitch(viewmux_ptr mux, view_ptr view) { + //mux->eject(mux, mux->current); // Get rid of the current view (should we really do this here?) + if (view) { + view->mux = mux; // In case it wasn't set before + if(view->entry) view->entry(view); // Call the entry hook if it exists + ViewSetUpdate(view); // Set flag to update next cycle + } + mux->current = view; +} + +void VmuxTick(viewmux_ptr mux) { + char key; + if (mux->current) { + if((key = fgetc(&mux->port->dev)) != EOF) + mux->current->key (mux->current, key); + } + if (mux->current) { // Check again, the view might have disappeared! + if (ViewGetUpdate(mux->current)) { + mux->current->update(mux->current, mux->port); + ViewClearUpdate(mux->current); + } + } + else { // What do you do if there's no view, currently? Ask for one! + VmuxSwitch(mux, mux->accept(mux)); + } +} + +view_ptr VmuxDefaultAcceptFunc(viewmux_ptr me) { + return 0; +} +void VmuxDefaultEjectFunc(viewmux_ptr me, view_ptr view) { + if (view) { + if (view->exit) view->exit(view); // Call the exit hook to let the view know it was ejected + view->mux = 0; // This may not be necessary + } + me->current = 0; // Now we don't have a view +} \ No newline at end of file From 5eb53294d504478832edda6db5f488086f515924 Mon Sep 17 00:00:00 2001 From: Billiam Crashkopf Date: Sat, 1 Feb 2014 18:55:28 -0800 Subject: [PATCH 2/7] Removed .DS_Store files --- Canbus/.DS_Store | Bin 6148 -> 0 bytes KSlibs/.DS_Store | Bin 6148 -> 0 bytes ModbusSlave/.DS_Store | Bin 6148 -> 0 bytes ModbusSlave/modbus_rtu_slave/.DS_Store | Bin 6148 -> 0 bytes PID_AutoTune_v0/.DS_Store | Bin 6148 -> 0 bytes PID_AutoTune_v0/Examples/.DS_Store | Bin 6148 -> 0 bytes PID_Beta6/.DS_Store | Bin 6148 -> 0 bytes PID_Beta6/Examples/.DS_Store | Bin 6148 -> 0 bytes SD/.DS_Store | Bin 6148 -> 0 bytes 9 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Canbus/.DS_Store delete mode 100644 KSlibs/.DS_Store delete mode 100644 ModbusSlave/.DS_Store delete mode 100644 ModbusSlave/modbus_rtu_slave/.DS_Store delete mode 100644 PID_AutoTune_v0/.DS_Store delete mode 100644 PID_AutoTune_v0/Examples/.DS_Store delete mode 100644 PID_Beta6/.DS_Store delete mode 100644 PID_Beta6/Examples/.DS_Store delete mode 100644 SD/.DS_Store diff --git a/Canbus/.DS_Store b/Canbus/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0+pd7D(eVrM}=F2!{QFd?1J2t>FMhfRtP1U3prDM-gL_&Tfn$K#fFk zBxs7|5`e8gb}L{BV4yqV%ZIu7zWdDXDq=)B&v?g(Jx;sR+c?YqJK)>_TYTaLhu{3y z<8ky&nG}!$Qa}nw0V!}%1**Ib4;MXEhe-h`a0?3f_o2}pd*PHApAHVu0uUDrhjAXg z1hIL5*bApbMrf8)Vp6SI3`;uWt@3)|l$dl_H6K9C%tQ3^)|36Qdk+;pO-- ck}|LPocq0SN(?&VK_}{Gz;%&Hfom)91G1zQf&c&j diff --git a/ModbusSlave/.DS_Store b/ModbusSlave/.DS_Store deleted file mode 100644 index 19272f3a0936b97bbdd045fd23a93a02fd69bd47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKyH3ME5S)b+k!V~}-Y@V6RusN~4+sR)8AT$5+Eu=bPhjhw&)9?Uv0M>LzoPAiD@4GMTrXoh9^NbUY*x>`8oc+z5+uK$X+=-9@K}ECr;%H7elWhemho3#Y{Rba03k zfVg5fjPvLvh|L4UzHmxpgl0)4Ce^CNu%t8IDz7h`5|a+A=ELe{s}9BDcAnoN9o8pm zlmb#0UTYiuneI8?bT`g}!Xe5r kG0HI)UXJf0Df61ox!)H~i9u&P=tTVtxGpj&@ZSpj0PUq1Q2+n{ diff --git a/ModbusSlave/modbus_rtu_slave/.DS_Store b/ModbusSlave/modbus_rtu_slave/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0+mc4$`2*J;o=-S;zy=?9#^E>r z<#@dGj`^FFl>$;g3P=GdAO+?W@ZL)s?h+NHfE17dKMMHwq0t?C;g}eo4u%*3hzq8} zxQiX_l2ER#E5jBafbt5vBy1b7TLcCoZI1=ybp{s{wE&s zf;SA<@7|w>r*RxSStbRffE17dQa}nEr9hR_^~F)Ah%5!9z$q%=--kwb>+mc4$`2*J;o=-S;zy=?9#^E>r z<#@dGj`^FFl>$;g3P=GdAO+?W@ZL)s?h+NHfE17dKMMHwq0t?C;g}eo4u%*3hzq8} zxQ-s>h%_lF_ZJX9u!@d?4kA3{VI8YCJtmh9Q} zd2Y2+oX-Gk^S-+R)&Q1tM|^mgn?HA-*;!?bNas5q@Pb#2c*gat`gFp%7r4VM25i6c zcW+PQ<9jQBev=0V(jMfPWtv-LV%=iSg-Rh!KFe zU^AHDXxOnQv9s3#Y`S!)o}jI@xMMu{fRYZ&42GiHcG{ z3LGnNp4+we{~P)b^ZzkPJ1HOq{*?l@7`DTPSE}ARyPWshM!%!2`1J0?au h=EmFcWfWyy^EIFM!YMK6%m6rKP8 diff --git a/SD/.DS_Store b/SD/.DS_Store deleted file mode 100644 index 4cf24832c696e80dc00e8a0e6255045c08891da5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKI|>3p3{6x}u(7n9D|mxJ^aNf&)J9maPz$@QJeNoF<%1}zmDtD&ByT30x5K<* zvk?)UpLa8niHMBghH|ygHG6K}v0i2r2**42a+1sLxNSCvPWAhQaffmSZ!gU=|IoZA zFiQof02QDDRDcS6rGWKb*k~NcNCl_>6?iCM--iM>tch))e>xC+1ON_@cEj3d31G1V zuqL*F$iOtHz@Tc57#eisOV-uIHZbU-IeciItT~~mpN{tzFBh$Wj8uRMyeiO-?Zo>3 z68>TSe X@w_Isflf!>=|KJrm@YIb@M{I`KV}uk From 37cab413db02be796f38f2f0eefe0daf43b5e14e Mon Sep 17 00:00:00 2001 From: crashkopf Date: Tue, 18 Feb 2014 23:45:40 -0800 Subject: [PATCH 3/7] Added H-bridge driver for VNH2SP30 --- KSlibs/gpio.c | 27 +++++++++++ KSlibs/gpio.h | 21 +++++++++ KSlibs/vnh_bridge.c | 108 ++++++++++++++++++++++++++++++++++++++++++++ KSlibs/vnh_bridge.h | 64 ++++++++++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 KSlibs/gpio.c create mode 100644 KSlibs/gpio.h create mode 100644 KSlibs/vnh_bridge.c create mode 100644 KSlibs/vnh_bridge.h diff --git a/KSlibs/gpio.c b/KSlibs/gpio.c new file mode 100644 index 0000000..718178a --- /dev/null +++ b/KSlibs/gpio.c @@ -0,0 +1,27 @@ +#include +#include "gpio.h" + +#ifndef _BV +#define _BV(bit) (1<<(bit)) +#endif + +// Set bit in DDR +void gpio_set_ddr(gpio_s* p){ + *(p->port - 1) |= _BV(p->pin); +} +// Clear bit in DDR +void gpio_clr_ddr(gpio_s* p){ + *(p->port - 1) &= 0xFF & ~_BV(p->pin); +} +// Set pin in PORT +void gpio_set_pin(gpio_s* p){ + *p->port |= _BV(p->pin); +} +// Clear pin in PORT +void gpio_clr_pin(gpio_s* p){ + *p->port &= 0xFF & ~_BV(p->pin); +} +// Get PIN value +unsigned char gpio_get_pin(gpio_s* p){ + return *(p->port - 2); +} \ No newline at end of file diff --git a/KSlibs/gpio.h b/KSlibs/gpio.h new file mode 100644 index 0000000..ba0889f --- /dev/null +++ b/KSlibs/gpio.h @@ -0,0 +1,21 @@ +#ifndef GPIO_H +#define GPIO_H +typedef struct gpio { + volatile uint8_t *port; + unsigned char pin; +} gpio_s; + +#ifdef __cplusplus +extern "C" { +#endif +void gpio_set_ddr(gpio_s*); // Set bit in DDR +void gpio_clr_ddr(gpio_s*); // Clear bit in DDR +void gpio_set_pin(gpio_s*); // Set pin in PORT +void gpio_clr_pin(gpio_s*); // Clear pin in PORT +unsigned char gpio_get_pin(gpio_s*); // Get PIN value + +#ifdef __cplusplus +} +#endif + +#endif // GPIO_H \ No newline at end of file diff --git a/KSlibs/vnh_bridge.c b/KSlibs/vnh_bridge.c new file mode 100644 index 0000000..9d80f0a --- /dev/null +++ b/KSlibs/vnh_bridge.c @@ -0,0 +1,108 @@ +#include +#include "vnh_bridge.h" +#include "adc.h" + + +void vnh_reset(vnh_s* v){ + // Set pins for switches and PWM as outputs + gpio_set_ddr(&v->mota); + gpio_set_ddr(&v->motb); + gpio_set_ddr(&v->pwm); + // Set enable pins as inputs + gpio_clr_ddr(&v->ena); + gpio_clr_ddr(&v->enb); + // Go into standby mode + vnh_standby(v); + // Clear the sample buffer + int x; + for (x=0; x < VNH_SAMPLES; x++) { + v->avg.samples[x] = 0; + } + v->avg.head = 0; + v->avg.accum = 0; +} +void vnh_forward(vnh_s* v){ + v->mod.duty = 0; + v->mod.count = 0; + // A=hi, B=lo + gpio_set_pin(&v->mota); + gpio_clr_pin(&v->motb); + // Enable outputs + gpio_set_pin(&v->ena); + gpio_set_pin(&v->enb); + // Turn on PWM + v->status.enabled = 1; +} +void vnh_reverse(vnh_s* v){ + v->mod.duty = 0; + v->mod.count = 0; + // A=lo, B=hi + gpio_clr_pin(&v->mota); + gpio_set_pin(&v->motb); + // Enable outputs + gpio_set_pin(&v->ena); + gpio_set_pin(&v->enb); + // Turn on PWM + v->status.enabled = 1; +} +void vnh_brake(vnh_s* v){ + // Max duty + v->mod.duty = v->mod.max; + // A=lo, B=lo + gpio_clr_pin(&v->mota); + gpio_clr_pin(&v->motb); + // Enable outputs + gpio_set_pin(&v->ena); + gpio_set_pin(&v->enb); +} +void vnh_standby(vnh_s* v){ + // Disable PWM + v->status.enabled = 0; + // Disable A, B + gpio_clr_pin(&v->ena); + gpio_clr_pin(&v->enb); + // Turn off PWM + gpio_clr_pin(&v->pwm); + // Toggle inputs to clear any faults + gpio_set_pin(&v->mota); + gpio_set_pin(&v->motb); + gpio_clr_pin(&v->mota); + gpio_clr_pin(&v->motb); +} + +void vnh_tick(vnh_s* v){ + if (v->avg.head < (VNH_SAMPLES - 1)) { + v->avg.accum -= v->avg.samples[++v->avg.head]; // Subtract sample at tail and advance head + v->avg.samples[v->avg.head] = ADC_ReadChanSync(v->csense_adc); // Overwrite old sample + v->avg.accum += v->avg.samples[v->avg.head]; // Add the latest sample + } + else { + // Roll over + v->avg.accum -= v->avg.samples[0]; // Subtract sample at tail and advance head + v->avg.samples[0] = ADC_ReadChanSync(v->csense_adc); // Overwrite old sample + v->avg.accum += v->avg.samples[0]; // Add the latest sample + v->avg.head = 0; + } + if (v->status.enabled) { + if (v->mod.count < v->mod.duty) gpio_set_pin(&v->pwm); + else gpio_clr_pin(&v->pwm); + v->mod.count++; + if (v->mod.count > v->mod.max) { + v->mod.count = 0; + v->mod.duty += v->mod.ramp; + } + } + else { + gpio_clr_pin(&v->pwm); + } +} + +unsigned int vnh_get_current(vnh_s* v){ + return (v->avg.accum >> VNH_DIVISOR); +} +vnh_status_s vnh_get_status(vnh_s *v){ + return v->status; +} + +void vnh_set_period(vnh_s* v, unsigned int period){} +void vnh_set_duty(vnh_s* v, unsigned int duty){} \ No newline at end of file diff --git a/KSlibs/vnh_bridge.h b/KSlibs/vnh_bridge.h new file mode 100644 index 0000000..f5dadfa --- /dev/null +++ b/KSlibs/vnh_bridge.h @@ -0,0 +1,64 @@ +/* + Driver API for VNH2SP30 +*/ + +#include "gpio.h" + +#define VNH_UAMP_UNIT (37538) // Unit for ADC to AMP conversion +#define VNH_PWM_PERIOD () +#define VNH_SAMPLES (64) +#define VNH_DIVISOR (6) + +typedef struct { + unsigned char enabled:1; // 0=standby, 1=go + unsigned char direction:1; // 0=FWD, 1=REV + unsigned char fault_A:1; // Fault detected on channel A + unsigned char fault_B:1; // Fault detected on channel B + unsigned char current:2; // 0=LOW, 1=MED, 2=HIGH + unsigned char limit:1; // 1 if over-current threshold reached +} vnh_status_s; + +typedef struct { + unsigned int head; + unsigned long accum; + unsigned int samples[VNH_SAMPLES]; +} vnh_sbuf_s; + +typedef struct { + unsigned int max; + unsigned int duty; + unsigned int count; + unsigned int ramp; +} vnh_mod_s; + +typedef struct { + vnh_status_s status; + gpio_s mota; + gpio_s motb; + gpio_s ena; + gpio_s enb; + gpio_s pwm; + unsigned int csense_adc; // ADC channel for current sense + unsigned int max_current; // Current limit, in milliamps + vnh_mod_s mod; // Modulation + vnh_sbuf_s avg; +} vnh_s; + +#ifdef __cplusplus +extern "C" { +#endif + +void vnh_reset(vnh_s*); // Reset, clear fault +void vnh_forward(vnh_s*); // A=hi, B=lo +void vnh_reverse(vnh_s*); // A=lo, B=hi +void vnh_brake(vnh_s*); // A=lo, B=lo +void vnh_standby(vnh_s*); // Disable A, B + +void vnh_tick(vnh_s*); + +unsigned int vnh_get_current(vnh_s*); // Returns average current +vnh_status_s vnh_get_status(vnh_s*); + +#ifdef __cplusplus +} +#endif \ No newline at end of file From 50dfb2f69abb9f7a3a47098839b9ea4dd6811020 Mon Sep 17 00:00:00 2001 From: crashkopf Date: Fri, 21 Feb 2014 11:12:41 -0800 Subject: [PATCH 4/7] Continued improvements to VNH bridge driver --- KSlibs/vnh_bridge.c | 197 ++++++++++++++++++++++++++++++++------------ KSlibs/vnh_bridge.h | 52 ++++++++---- 2 files changed, 178 insertions(+), 71 deletions(-) diff --git a/KSlibs/vnh_bridge.c b/KSlibs/vnh_bridge.c index 9d80f0a..8feb6d5 100644 --- a/KSlibs/vnh_bridge.c +++ b/KSlibs/vnh_bridge.c @@ -2,8 +2,9 @@ #include "vnh_bridge.h" #include "adc.h" - void vnh_reset(vnh_s* v){ + // Go into standby mode + vnh_standby(v); // Set pins for switches and PWM as outputs gpio_set_ddr(&v->mota); gpio_set_ddr(&v->motb); @@ -11,98 +12,188 @@ void vnh_reset(vnh_s* v){ // Set enable pins as inputs gpio_clr_ddr(&v->ena); gpio_clr_ddr(&v->enb); - // Go into standby mode - vnh_standby(v); - // Clear the sample buffer - int x; - for (x=0; x < VNH_SAMPLES; x++) { - v->avg.samples[x] = 0; - } - v->avg.head = 0; - v->avg.accum = 0; + // Reset averaging function + vnh_avg_reset(v); + // Reset status flags + v->status.fault_A = 0; + v->status.fault_B = 0; + v->status.limit = 0; + // Toggle motor outputs to clear any faults + gpio_set_pin(&v->mota); + gpio_set_pin(&v->motb); + gpio_clr_pin(&v->mota); + gpio_clr_pin(&v->motb); +} + +void vnh_standby(vnh_s* v){ + // Set status to stop PWM + v->status.mode = VNH_STANDBY; + // Disable A, B + gpio_clr_pin(&v->ena); + gpio_clr_pin(&v->enb); + // Reset PWM counters + vnh_pwm_reset(v); } + void vnh_forward(vnh_s* v){ - v->mod.duty = 0; - v->mod.count = 0; + // Momentarily go to standby + vnh_standby(v); // A=hi, B=lo gpio_set_pin(&v->mota); gpio_clr_pin(&v->motb); // Enable outputs gpio_set_pin(&v->ena); gpio_set_pin(&v->enb); - // Turn on PWM - v->status.enabled = 1; -} + // Set status + v->status.mode = VNH_FORWARD; +} + void vnh_reverse(vnh_s* v){ - v->mod.duty = 0; - v->mod.count = 0; + // Momentarily go to standby + vnh_standby(v); // A=lo, B=hi gpio_clr_pin(&v->mota); gpio_set_pin(&v->motb); // Enable outputs gpio_set_pin(&v->ena); gpio_set_pin(&v->enb); - // Turn on PWM - v->status.enabled = 1; + // Set status + v->status.mode = VNH_REVERSE; } + void vnh_brake(vnh_s* v){ - // Max duty - v->mod.duty = v->mod.max; + // Momentarily go to standby + vnh_standby(v); // A=lo, B=lo gpio_clr_pin(&v->mota); gpio_clr_pin(&v->motb); // Enable outputs gpio_set_pin(&v->ena); gpio_set_pin(&v->enb); + v->status.mode = VNH_BRAKE; +} + +void vnh_avg_reset(vnh_s* v){ + int x; + // Clear sample buffer + for (x=0; x < VNH_SAMPLES; x++) { + v->avg.samples[x] = 0; + } + v->avg.head = 0; + v->avg.accum = 0; } -void vnh_standby(vnh_s* v){ - // Disable PWM - v->status.enabled = 0; - // Disable A, B - gpio_clr_pin(&v->ena); - gpio_clr_pin(&v->enb); - // Turn off PWM + +void vnh_pwm_reset(vnh_s* v){ + // Go straight to target duty in fixed mode + if (v->mod.mode == VNH_PWM_FIXED) + v->mod.duty = v->mod.target; + // Otherwise, start at zero. PWM code will ramp up + else v->mod.duty = 0; + // Reset cycle counter + v->mod.count = 0; + // Turn off PWM output gpio_clr_pin(&v->pwm); - // Toggle inputs to clear any faults - gpio_set_pin(&v->mota); - gpio_set_pin(&v->motb); - gpio_clr_pin(&v->mota); - gpio_clr_pin(&v->motb); -} +} void vnh_tick(vnh_s* v){ + static unsigned int adcpre = 0; + vnh_pwm_tick(v); + // ADC sampling is pre-scaled + if (++adcpre > 10){ + vnh_adc_tick(v); + adcpre=0; + } + // Fault checks + v->status.fault_A = !gpio_get_pin(&v->ena); + v->status.fault_B = !gpio_get_pin(&v->enb); + // Current limiting + if (v->climit) { + // If we're over the limit current go into limit mode + if (v->avg.avg > v->climit) { + v->status.limit = 1; + } + // If we're in limit mode but no longer above the limit current + // (minus our hysteresis value), go back to normal mode + if (v->status.limit && (v->avg.avg < (v->climit - v->chyst))) { + v->status.limit = 0; + } + } +} + +void vnh_pwm_tick(vnh_s* v){ + vnh_status_s stat = v->status; + switch (stat.mode) { + case VNH_STANDBY: + // Just let it go... + gpio_clr_pin(&v->pwm); + break; + case VNH_BRAKE: + // Clamp down hard! + gpio_set_pin(&v->pwm); + break; + case VNH_FORWARD: + case VNH_REVERSE: + default: + // Everything else in modulation + if (v->mod.mode != VNH_PWM_NONE) { + if (v->mod.count < v->mod.duty) + gpio_set_pin(&v->pwm); + else + gpio_clr_pin(&v->pwm); + v->mod.count++; + if (v->mod.count > VNH_MOD_MAX) + v->mod.count = 0; + // Only change duty at the beginning of a cycle + if (v->mod.count == 0) { + // Ramp down in limit mode + if (v->status.limit) { + if (v->mod.duty < v->mod.ramp) v->mod.duty = 0; + else v->mod.duty -= v->mod.ramp; + } + // Otherwise, ramp towards target duty + else { + if (v->mod.duty < v->mod.target) { + if (v->mod.target - v->mod.duty < v->mod.ramp) + v->mod.duty = v->mod.target; + else v->mod.duty += v->mod.ramp; + } + else { + if (v->mod.duty - v->mod.target < v->mod.ramp) + v->mod.duty = v->mod.target; + else v->mod.duty -= v->mod.ramp; + } + } + } + } + else gpio_set_pin(&v->pwm); + break; + } +} +void vnh_adc_tick(vnh_s* v){ if (v->avg.head < (VNH_SAMPLES - 1)) { - v->avg.accum -= v->avg.samples[++v->avg.head]; // Subtract sample at tail and advance head - v->avg.samples[v->avg.head] = ADC_ReadChanSync(v->csense_adc); // Overwrite old sample - v->avg.accum += v->avg.samples[v->avg.head]; // Add the latest sample + v->avg.accum -= v->avg.samples[++v->avg.head]; // Subtract sample at tail and advance head + v->avg.samples[v->avg.head] = ADC_ReadChanSync(v->adc); // Overwrite old sample + v->avg.accum += v->avg.samples[v->avg.head]; // Add the latest sample } else { // Roll over - v->avg.accum -= v->avg.samples[0]; // Subtract sample at tail and advance head - v->avg.samples[0] = ADC_ReadChanSync(v->csense_adc); // Overwrite old sample - v->avg.accum += v->avg.samples[0]; // Add the latest sample + v->avg.accum -= v->avg.samples[0]; // Subtract sample at tail and advance head + v->avg.samples[0] = ADC_ReadChanSync(v->adc); // Overwrite old sample + v->avg.accum += v->avg.samples[0]; // Add the latest sample v->avg.head = 0; } - if (v->status.enabled) { - if (v->mod.count < v->mod.duty) gpio_set_pin(&v->pwm); - else gpio_clr_pin(&v->pwm); - v->mod.count++; - if (v->mod.count > v->mod.max) { - v->mod.count = 0; - v->mod.duty += v->mod.ramp; - } - } - else { - gpio_clr_pin(&v->pwm); - } + v->avg.avg = (v->avg.accum >> VNH_DIVISOR); // Divide the accumulator } unsigned int vnh_get_current(vnh_s* v){ - return (v->avg.accum >> VNH_DIVISOR); + return v->avg.avg; } vnh_status_s vnh_get_status(vnh_s *v){ return v->status; } +uint8_t vnh_get_fault(vnh_s* v) { + return v->status.fault_A | v->status.fault_B | v->status.limit; +} void vnh_set_period(vnh_s* v, unsigned int period){} void vnh_set_duty(vnh_s* v, unsigned int duty){} \ No newline at end of file diff --git a/KSlibs/vnh_bridge.h b/KSlibs/vnh_bridge.h index f5dadfa..3710ad3 100644 --- a/KSlibs/vnh_bridge.h +++ b/KSlibs/vnh_bridge.h @@ -4,31 +4,42 @@ #include "gpio.h" -#define VNH_UAMP_UNIT (37538) // Unit for ADC to AMP conversion -#define VNH_PWM_PERIOD () -#define VNH_SAMPLES (64) -#define VNH_DIVISOR (6) +#define VNH_ADC_UNIT (37538) // Unit for ADC to mA conversion +#define VNH_SAMPLES (64) // Number of samples for AVG function +#define VNH_DIVISOR (6) // Bit shift divisor x, y / 2^x +#define VNH_MOD_MAX (100) // PWM counter maximum, 100 for duty in % +#define VNH_FAULT_MASK (0x4C) + +#define VNH_STANDBY 0 +#define VNH_FORWARD 1 +#define VNH_REVERSE 2 +#define VNH_BRAKE 3 +#define VNH_LIMIT 1 + +#define VNH_PWM_NONE 0 +#define VNH_PWM_FIXED 1 +#define VNH_PWM_SOFT 2 typedef struct { - unsigned char enabled:1; // 0=standby, 1=go - unsigned char direction:1; // 0=FWD, 1=REV - unsigned char fault_A:1; // Fault detected on channel A - unsigned char fault_B:1; // Fault detected on channel B - unsigned char current:2; // 0=LOW, 1=MED, 2=HIGH - unsigned char limit:1; // 1 if over-current threshold reached + uint8_t mode:2; // 0=STDBY, 1=FWD, 2=REV, 3=BRK + uint8_t fault_A:1; // Fault detected on channel A + uint8_t fault_B:1; // Fault detected on channel B + uint8_t limit:1; // 1 if over-current threshold reached } vnh_status_s; typedef struct { + unsigned int avg; unsigned int head; unsigned long accum; unsigned int samples[VNH_SAMPLES]; } vnh_sbuf_s; typedef struct { - unsigned int max; - unsigned int duty; - unsigned int count; - unsigned int ramp; + uint8_t mode; // PWM mode: none, fixed, or soft start + uint8_t target; // Target PWM duty + uint8_t duty; // Current duty + uint8_t count; // Cycle counter + uint8_t ramp; // Ramp rate } vnh_mod_s; typedef struct { @@ -38,10 +49,11 @@ typedef struct { gpio_s ena; gpio_s enb; gpio_s pwm; - unsigned int csense_adc; // ADC channel for current sense - unsigned int max_current; // Current limit, in milliamps - vnh_mod_s mod; // Modulation - vnh_sbuf_s avg; + unsigned int adc; // ADC channel for current sense + unsigned int climit; // Current limit, in ADC units + unsigned int chyst; // Current limit hysteresis + vnh_mod_s mod; // Modulation + vnh_sbuf_s avg; // Averaging sample buffer } vnh_s; #ifdef __cplusplus @@ -58,6 +70,10 @@ void vnh_tick(vnh_s*); unsigned int vnh_get_current(vnh_s*); // Returns average current vnh_status_s vnh_get_status(vnh_s*); +uint8_t vnh_get_fault(vnh_s*); + +void vnh_avg_reset(vnh_s*); +void vnh_pwm_reset(vnh_s*); #ifdef __cplusplus } From b731c233d12aad2fab559889b6e97592121066c8 Mon Sep 17 00:00:00 2001 From: crashkopf Date: Tue, 25 Feb 2014 13:47:45 -0800 Subject: [PATCH 5/7] Created modified Servo library We need to use timer 5 elsewhere, and the stock Servo library selfishly takes over all available timers. --- Servo/Servo.cpp | 337 +++++++++++++++++++++++++++++++++ Servo/Servo.h | 127 +++++++++++++ Servo/examples/Knob/Knob.pde | 22 +++ Servo/examples/Sweep/Sweep.pde | 31 +++ Servo/keywords.txt | 24 +++ 5 files changed, 541 insertions(+) create mode 100644 Servo/Servo.cpp create mode 100644 Servo/Servo.h create mode 100644 Servo/examples/Knob/Knob.pde create mode 100644 Servo/examples/Sweep/Sweep.pde create mode 100644 Servo/keywords.txt diff --git a/Servo/Servo.cpp b/Servo/Servo.cpp new file mode 100644 index 0000000..71e3377 --- /dev/null +++ b/Servo/Servo.cpp @@ -0,0 +1,337 @@ +/* + Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Copyright (c) 2009 Michael Margolis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + + A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method. + The servos are pulsed in the background using the value most recently written using the write() method + + Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached. + Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four. + + The methods are: + + Servo - Class for manipulating servo motors connected to Arduino pins. + + attach(pin ) - Attaches a servo motor to an i/o pin. + attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds + default min is 544, max is 2400 + + write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) + writeMicroseconds() - Sets the servo pulse width in microseconds + read() - Gets the last written servo pulse width as an angle between 0 and 180. + readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release) + attached() - Returns true if there is a servo attached. + detach() - Stops an attached servos from pulsing its i/o pin. + +*/ + +#include +#include + +#include "Servo.h" + +#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009 +#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds + + +#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009 + +//#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER) + +static servo_t servos[MAX_SERVOS]; // static array of servo structures +static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) + +uint8_t ServoCount = 0; // the total number of attached servos + + +// convenience macros +#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo +#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer +#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel +#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel + +#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo +#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo + +/************ static functions common to all instances ***********************/ + +static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA) +{ + if( Channel[timer] < 0 ) + *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer + else{ + if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true ) + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated + } + + Channel[timer]++; // increment to the next channel + if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { + *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks; + if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high + } + else { + // finished all channels so wait for the refresh period to expire before starting over + if( (unsigned)*TCNTn < (usToTicks(REFRESH_INTERVAL) + 4) ) // allow a few ticks to ensure the next OCR1A not missed + *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL); + else + *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed + Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + } +} + +#ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform +// Interrupt handlers for Arduino +#if defined(_useTimer1) +SIGNAL (TIMER1_COMPA_vect) +{ + handle_interrupts(_timer1, &TCNT1, &OCR1A); +} +#endif + +#if defined(_useTimer3) +SIGNAL (TIMER3_COMPA_vect) +{ + handle_interrupts(_timer3, &TCNT3, &OCR3A); +} +#endif + +#if defined(_useTimer4) +SIGNAL (TIMER4_COMPA_vect) +{ + handle_interrupts(_timer4, &TCNT4, &OCR4A); +} +#endif + +#if defined(_useTimer5) +SIGNAL (TIMER5_COMPA_vect) +{ + handle_interrupts(_timer5, &TCNT5, &OCR5A); +} +#endif + +#elif defined WIRING +// Interrupt handlers for Wiring +#if defined(_useTimer1) +void Timer1Service() +{ + handle_interrupts(_timer1, &TCNT1, &OCR1A); +} +#endif +#if defined(_useTimer3) +void Timer3Service() +{ + handle_interrupts(_timer3, &TCNT3, &OCR3A); +} +#endif +#endif + + +static void initISR(timer16_Sequence_t timer) +{ +#if defined (_useTimer1) + if(timer == _timer1) { + TCCR1A = 0; // normal counting mode + TCCR1B = _BV(CS11); // set prescaler of 8 + TCNT1 = 0; // clear the timer count +#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__) + TIFR |= _BV(OCF1A); // clear any pending interrupts; + TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt +#else + // here if not ATmega8 or ATmega128 + TIFR1 |= _BV(OCF1A); // clear any pending interrupts; + TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt +#endif +#if defined(WIRING) + timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service); +#endif + } +#endif + +#if defined (_useTimer3) + if(timer == _timer3) { + TCCR3A = 0; // normal counting mode + TCCR3B = _BV(CS31); // set prescaler of 8 + TCNT3 = 0; // clear the timer count +#if defined(__AVR_ATmega128__) + TIFR |= _BV(OCF3A); // clear any pending interrupts; + ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt +#else + TIFR3 = _BV(OCF3A); // clear any pending interrupts; + TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt +#endif +#if defined(WIRING) + timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only +#endif + } +#endif + +#if defined (_useTimer4) + if(timer == _timer4) { + TCCR4A = 0; // normal counting mode + TCCR4B = _BV(CS41); // set prescaler of 8 + TCNT4 = 0; // clear the timer count + TIFR4 = _BV(OCF4A); // clear any pending interrupts; + TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt + } +#endif + +#if defined (_useTimer5) + if(timer == _timer5) { + TCCR5A = 0; // normal counting mode + TCCR5B = _BV(CS51); // set prescaler of 8 + TCNT5 = 0; // clear the timer count + TIFR5 = _BV(OCF5A); // clear any pending interrupts; + TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt + } +#endif +} + +static void finISR(timer16_Sequence_t timer) +{ + //disable use of the given timer +#if defined WIRING // Wiring + if(timer == _timer1) { + #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) + TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt + #else + TIMSK &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt + #endif + timerDetach(TIMER1OUTCOMPAREA_INT); + } + else if(timer == _timer3) { + #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) + TIMSK3 &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt + #else + ETIMSK &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt + #endif + timerDetach(TIMER3OUTCOMPAREA_INT); + } +#else + //For arduino - in future: call here to a currently undefined function to reset the timer +#endif +} + +static boolean isTimerActive(timer16_Sequence_t timer) +{ + // returns true if any servo is active on this timer + for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) { + if(SERVO(timer,channel).Pin.isActive == true) + return true; + } + return false; +} + + +/****************** end of static functions ******************************/ + +Servo::Servo() +{ + if( ServoCount < MAX_SERVOS) { + this->servoIndex = ServoCount++; // assign a servo index to this instance + servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009 + } + else + this->servoIndex = INVALID_SERVO ; // too many servos +} + +uint8_t Servo::attach(int pin) +{ + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) +{ + if(this->servoIndex < MAX_SERVOS ) { + pinMode( pin, OUTPUT) ; // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128 + this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS + this->max = (MAX_PULSE_WIDTH - max)/4; + // initialize the timer if it has not already been initialized + timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) + initISR(timer); + servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive + } + return this->servoIndex ; +} + +void Servo::detach() +{ + servos[this->servoIndex].Pin.isActive = false; + timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) { + finISR(timer); + } +} + +void Servo::write(int value) +{ + if(value < MIN_PULSE_WIDTH) + { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) + if(value < 0) value = 0; + if(value > 180) value = 180; + value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX()); + } + this->writeMicroseconds(value); +} + +void Servo::writeMicroseconds(int value) +{ + // calculate and store the values for the given channel + byte channel = this->servoIndex; + if( (channel >= 0) && (channel < MAX_SERVOS) ) // ensure channel is valid + { + if( value < SERVO_MIN() ) // ensure pulse width is valid + value = SERVO_MIN(); + else if( value > SERVO_MAX() ) + value = SERVO_MAX(); + + value = value - TRIM_DURATION; + value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009 + + uint8_t oldSREG = SREG; + cli(); + servos[channel].ticks = value; + SREG = oldSREG; + } +} + +int Servo::read() // return the value as degrees +{ + return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180); +} + +int Servo::readMicroseconds() +{ + unsigned int pulsewidth; + if( this->servoIndex != INVALID_SERVO ) + pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION ; // 12 aug 2009 + else + pulsewidth = 0; + + return pulsewidth; +} + +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive ; +} diff --git a/Servo/Servo.h b/Servo/Servo.h new file mode 100644 index 0000000..d62daeb --- /dev/null +++ b/Servo/Servo.h @@ -0,0 +1,127 @@ +/* + Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Copyright (c) 2009 Michael Margolis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + + A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method. + The servos are pulsed in the background using the value most recently written using the write() method + + Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached. + Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four. + The sequence used to sieze timers is defined in timers.h + + The methods are: + + Servo - Class for manipulating servo motors connected to Arduino pins. + + attach(pin ) - Attaches a servo motor to an i/o pin. + attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds + default min is 544, max is 2400 + + write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) + writeMicroseconds() - Sets the servo pulse width in microseconds + read() - Gets the last written servo pulse width as an angle between 0 and 180. + readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release) + attached() - Returns true if there is a servo attached. + detach() - Stops an attached servos from pulsing its i/o pin. + */ + +#ifndef Servo_h +#define Servo_h + +#include + +/* + * Defines for 16 bit timers used with Servo library + * + * If _useTimerX is defined then TimerX is a 16 bit timer on the curent board + * timer16_Sequence_t enumerates the sequence that the timers should be allocated + * _Nbr_16timers indicates how many 16 bit timers are available. + * + */ + +// Say which 16 bit timers can be used and in what order +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +//#define _useTimer5 +#define _useTimer1 +#define _useTimer3 +#define _useTimer4 +typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ; + +#elif defined(__AVR_ATmega32U4__) +#define _useTimer3 +#define _useTimer1 +typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ; + +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +#define _useTimer3 +#define _useTimer1 +typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ; + +#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) +#define _useTimer3 +#define _useTimer1 +typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ; + +#else // everything else +#define _useTimer1 +typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t ; +#endif + +#define Servo_VERSION 2 // software version of this library + +#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo +#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo +#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached +#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds + +#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer +#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER) + +#define INVALID_SERVO 255 // flag indicating an invalid servo index + +typedef struct { + uint8_t nbr :6 ; // a pin number from 0 to 63 + uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false +} ServoPin_t ; + +typedef struct { + ServoPin_t Pin; + unsigned int ticks; +} servo_t; + +class Servo +{ +public: + Servo(); + uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure + uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. + void detach(); + void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds + void writeMicroseconds(int value); // Write pulse width in microseconds + int read(); // returns current pulse width as an angle between 0 and 180 degrees + int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release) + bool attached(); // return true if this servo is attached, otherwise false +private: + uint8_t servoIndex; // index into the channel data for this servo + int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH + int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH +}; + +#endif \ No newline at end of file diff --git a/Servo/examples/Knob/Knob.pde b/Servo/examples/Knob/Knob.pde new file mode 100644 index 0000000..886e107 --- /dev/null +++ b/Servo/examples/Knob/Knob.pde @@ -0,0 +1,22 @@ +// Controlling a servo position using a potentiometer (variable resistor) +// by Michal Rinott + +#include + +Servo myservo; // create servo object to control a servo + +int potpin = 0; // analog pin used to connect the potentiometer +int val; // variable to read the value from the analog pin + +void setup() +{ + myservo.attach(9); // attaches the servo on pin 9 to the servo object +} + +void loop() +{ + val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023) + val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180) + myservo.write(val); // sets the servo position according to the scaled value + delay(15); // waits for the servo to get there +} diff --git a/Servo/examples/Sweep/Sweep.pde b/Servo/examples/Sweep/Sweep.pde new file mode 100644 index 0000000..fb326e7 --- /dev/null +++ b/Servo/examples/Sweep/Sweep.pde @@ -0,0 +1,31 @@ +// Sweep +// by BARRAGAN +// This example code is in the public domain. + + +#include + +Servo myservo; // create servo object to control a servo + // a maximum of eight servo objects can be created + +int pos = 0; // variable to store the servo position + +void setup() +{ + myservo.attach(9); // attaches the servo on pin 9 to the servo object +} + + +void loop() +{ + for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees + { // in steps of 1 degree + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position + } + for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees + { + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position + } +} diff --git a/Servo/keywords.txt b/Servo/keywords.txt new file mode 100644 index 0000000..ca5ba79 --- /dev/null +++ b/Servo/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map Servo +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Servo KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +attach KEYWORD2 +detach KEYWORD2 +write KEYWORD2 +read KEYWORD2 +attached KEYWORD2 +writeMicroseconds KEYWORD2 +readMicroseconds KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### From 6839c402609a0f9d36f2ea106cf0637893a94f0a Mon Sep 17 00:00:00 2001 From: crashkopf Date: Thu, 27 Feb 2014 21:36:52 -0800 Subject: [PATCH 6/7] Bug fixes with vnh_bridge and Servo vnh_bridge - moved ADC handling to its own API function Servo - Fixed an issue where the timer5 was still being used for the servo. --- KSlibs/vnh_bridge.c | 31 +++++++++++++++---------------- KSlibs/vnh_bridge.h | 2 ++ Servo/Servo.h | 3 ++- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/KSlibs/vnh_bridge.c b/KSlibs/vnh_bridge.c index 8feb6d5..3ce805a 100644 --- a/KSlibs/vnh_bridge.c +++ b/KSlibs/vnh_bridge.c @@ -99,25 +99,13 @@ void vnh_tick(vnh_s* v){ static unsigned int adcpre = 0; vnh_pwm_tick(v); // ADC sampling is pre-scaled - if (++adcpre > 10){ - vnh_adc_tick(v); - adcpre=0; - } + // if (++adcpre > 10){ + // vnh_adc_tick(v); + // adcpre=0; + // } // Fault checks v->status.fault_A = !gpio_get_pin(&v->ena); v->status.fault_B = !gpio_get_pin(&v->enb); - // Current limiting - if (v->climit) { - // If we're over the limit current go into limit mode - if (v->avg.avg > v->climit) { - v->status.limit = 1; - } - // If we're in limit mode but no longer above the limit current - // (minus our hysteresis value), go back to normal mode - if (v->status.limit && (v->avg.avg < (v->climit - v->chyst))) { - v->status.limit = 0; - } - } } void vnh_pwm_tick(vnh_s* v){ @@ -183,6 +171,17 @@ void vnh_adc_tick(vnh_s* v){ v->avg.head = 0; } v->avg.avg = (v->avg.accum >> VNH_DIVISOR); // Divide the accumulator + if (v->climit) { + // If we're over the limit current go into limit mode + if (v->avg.avg > v->climit) { + v->status.limit = 1; + } + // If we're in limit mode but no longer above the limit current + // (minus our hysteresis value), go back to normal mode + if (v->status.limit && (v->avg.avg < (v->climit - v->chyst))) { + v->status.limit = 0; + } + } } unsigned int vnh_get_current(vnh_s* v){ diff --git a/KSlibs/vnh_bridge.h b/KSlibs/vnh_bridge.h index 3710ad3..43b674a 100644 --- a/KSlibs/vnh_bridge.h +++ b/KSlibs/vnh_bridge.h @@ -67,6 +67,8 @@ void vnh_brake(vnh_s*); // A=lo, B=lo void vnh_standby(vnh_s*); // Disable A, B void vnh_tick(vnh_s*); +void vnh_adc_tick(vnh_s* v); +void vnh_pwm_tick(vnh_s* v); unsigned int vnh_get_current(vnh_s*); // Returns average current vnh_status_s vnh_get_status(vnh_s*); diff --git a/Servo/Servo.h b/Servo/Servo.h index d62daeb..969ebc9 100644 --- a/Servo/Servo.h +++ b/Servo/Servo.h @@ -62,7 +62,8 @@ #define _useTimer1 #define _useTimer3 #define _useTimer4 -typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ; +//typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ; +typedef enum { _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ; #elif defined(__AVR_ATmega32U4__) #define _useTimer3 From 8c686c87e4a01ba4aa3fcdcc513c6ea32c795216 Mon Sep 17 00:00:00 2001 From: crashkopf Date: Wed, 5 Mar 2014 14:51:32 -0800 Subject: [PATCH 7/7] Added integer math convenience functions. See intmath.h --- KSlibs/intmath.c | 18 ++++++++++++++++++ KSlibs/intmath.h | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 KSlibs/intmath.c create mode 100644 KSlibs/intmath.h diff --git a/KSlibs/intmath.c b/KSlibs/intmath.c new file mode 100644 index 0000000..7d9fc9c --- /dev/null +++ b/KSlibs/intmath.c @@ -0,0 +1,18 @@ +unsigned u_sublim (unsigned minuend, unsigned subtrahend, unsigned limit) { + unsigned x; + if (minuend < limit) return limit; // Minuend must already be > limit + if (subtrahend > minuend) return limit; // Avoid unsigned underflow (result < 0) + x = minuend - subtrahend; + if (x > limit) return x; + else return limit; +} + +unsigned u_addlim (unsigned augend, unsigned addend, unsigned limit) { + unsigned x; + if (augend > limit) return limit; // Augend must be < limit + if (addend > limit) return limit; // Addend must be < limit + x = augend + addend; + if (x < addend) return limit; // Overflow must have occurred (usually undefined behavior) + if (x < limit) return x; + else return limit; +} \ No newline at end of file diff --git a/KSlibs/intmath.h b/KSlibs/intmath.h new file mode 100644 index 0000000..3ca78d2 --- /dev/null +++ b/KSlibs/intmath.h @@ -0,0 +1,17 @@ +/* + intmath.h - integer math convenience functions +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Subtract and limit the result +unsigned u_sublim (unsigned minuend, unsigned subtrahend, unsigned limit); + +// Add and limit the result +unsigned u_addlim (unsigned augend, unsigned addend, unsigned limit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file