seesaw/source/AOKeypad.cpp
2018-07-31 11:25:41 -04:00

350 lines
No EOL
12 KiB
C++

/*******************************************************************************
* Copyright (C) Dean Miller
* All rights reserved.
*
* This program is open source software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* 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.
******************************************************************************/
#include "qpcpp.h"
#include "qp_extras.h"
#include "hsm_id.h"
#include "AOKeypad.h"
#include "event.h"
#include "SeesawConfig.h"
#include "bsp_gpio.h"
#include "bsp_sercom.h"
#include "bsp_adc.h"
Q_DEFINE_THIS_FILE
using namespace FW;
#define KEYPAD_MAX_ROWS 4
#define KEYPAD_MAX_COLS 4
#define KEYPAD_INPUT_MASK (((uint64_t)CONFIG_KEYPAD_COL0 << CONFIG_KEYPAD_COL0_PIN) \
| ((uint64_t)CONFIG_KEYPAD_COL1 << CONFIG_KEYPAD_COL1_PIN) \
| ((uint64_t)CONFIG_KEYPAD_COL2 << CONFIG_KEYPAD_COL2_PIN) \
| ((uint64_t)CONFIG_KEYPAD_COL3 << CONFIG_KEYPAD_COL3_PIN))
#define KEYPAD_OUTPUT_MASK (((uint64_t)CONFIG_KEYPAD_ROW0 << CONFIG_KEYPAD_ROW0_PIN) \
| ((uint64_t)CONFIG_KEYPAD_ROW1 << CONFIG_KEYPAD_ROW1_PIN) \
| ((uint64_t)CONFIG_KEYPAD_ROW2 << CONFIG_KEYPAD_ROW2_PIN) \
| ((uint64_t)CONFIG_KEYPAD_ROW3 << CONFIG_KEYPAD_ROW3_PIN))
#define KEYPAD_ACTIVE_ROWS ( (CONFIG_KEYPAD_ROW3 << 3) | (CONFIG_KEYPAD_ROW2 << 2) \
| (CONFIG_KEYPAD_ROW1 << 1) | (CONFIG_KEYPAD_ROW0) )
#define KEYPAD_ACTIVE_COLS ( (CONFIG_KEYPAD_COL3 << 3) | (CONFIG_KEYPAD_COL2 << 2) \
| (CONFIG_KEYPAD_COL1 << 1) | (CONFIG_KEYPAD_COL0) )
#define KEYPAD_EVENT_TO_ROW(e) ( (e)/8 )
#define KEYPAD_EVENT_TO_COL(e) ( (e)%8 )
#define KEYPAD_EVENT(r, c) ( (r)*8 + (c) )
#define KEYPAD_HIGH (1 << KEYPAD_EDGE_HIGH)
#define KEYPAD_LOW (1 << KEYPAD_EDGE_LOW)
#define KEYPAD_RISING (1 << KEYPAD_EDGE_RISING)
#define KEYPAD_FALLING (1 << KEYPAD_EDGE_FALLING)
static const uint32_t keypad_input_masks[] = {
((uint32_t)CONFIG_KEYPAD_COL0 << CONFIG_KEYPAD_COL0_PIN),
((uint32_t)CONFIG_KEYPAD_COL1 << CONFIG_KEYPAD_COL1_PIN),
((uint32_t)CONFIG_KEYPAD_COL2 << CONFIG_KEYPAD_COL2_PIN),
((uint32_t)CONFIG_KEYPAD_COL3 << CONFIG_KEYPAD_COL3_PIN)
};
static const uint32_t keypad_output_masks[] = {
((uint32_t)CONFIG_KEYPAD_ROW0 << CONFIG_KEYPAD_ROW0_PIN),
((uint32_t)CONFIG_KEYPAD_ROW1 << CONFIG_KEYPAD_ROW1_PIN),
((uint32_t)CONFIG_KEYPAD_ROW2 << CONFIG_KEYPAD_ROW2_PIN),
((uint32_t)CONFIG_KEYPAD_ROW3 << CONFIG_KEYPAD_ROW3_PIN)
};
AOKeypad::AOKeypad() :
QActive((QStateHandler)&AOKeypad::InitialPseudoState),
m_id(AO_KEYPAD), m_name("Keypad"),m_syncTimer(this, KEYPAD_SYNC) {}
QState AOKeypad::InitialPseudoState(AOKeypad * const me, QEvt const * const e) {
(void)e;
me->subscribe(KEYPAD_START_REQ);
me->subscribe(KEYPAD_STOP_REQ);
me->subscribe(KEYPAD_SYNC);
me->subscribe(KEYPAD_WRITE_REG_REQ);
me->subscribe(KEYPAD_READ_REG_REQ);
return Q_TRAN(&AOKeypad::Root);
}
QState AOKeypad::Root(AOKeypad * const me, QEvt const * const e) {
QState status;
switch (e->sig) {
case Q_ENTRY_SIG: {
LOG_EVENT(e);
status = Q_HANDLED();
break;
}
case Q_EXIT_SIG: {
LOG_EVENT(e);
status = Q_HANDLED();
break;
}
case Q_INIT_SIG: {
status = Q_TRAN(&AOKeypad::Stopped);
break;
}
case KEYPAD_STOP_REQ: {
LOG_EVENT(e);
status = Q_TRAN(&AOKeypad::Stopped);
break;
}
default: {
status = Q_SUPER(&QHsm::top);
break;
}
}
return status;
}
QState AOKeypad::Stopped(AOKeypad * const me, QEvt const * const e) {
QState status;
switch (e->sig) {
case Q_ENTRY_SIG: {
LOG_EVENT(e);
status = Q_HANDLED();
break;
}
case Q_EXIT_SIG: {
LOG_EVENT(e);
status = Q_HANDLED();
break;
}
case KEYPAD_STOP_REQ: {
LOG_EVENT(e);
Evt const &req = EVT_CAST(*e);
Evt *evt = new KeypadStopCfm(req.GetSeq(), ERROR_SUCCESS);
QF::PUBLISH(evt, me);
status = Q_HANDLED();
break;
}
case KEYPAD_START_REQ: {
LOG_EVENT(e);
//set inputs
uint32_t mask = (uint32_t)KEYPAD_INPUT_MASK;
gpio_dirclr_bulk(PORTA, mask);
gpio_pullenset_bulk(mask);
gpio_outclr_bulk(PORTA, mask);
//set outputs
mask = (uint32_t)KEYPAD_OUTPUT_MASK;
gpio_dirset_bulk(PORTA, mask);
gpio_outclr_bulk(PORTA, mask);
memset(me->m_state, 0, 64); //initialize state array to 0
me->m_status.reg = 0;
me->m_inten.reg = 0;
KeypadStartReq const &r = static_cast<KeypadStartReq const &>(*e);
me->m_fifo = r.getFifo();
me->m_fifo->Reset();
Evt const &req = EVT_CAST(*e);
Evt *evt = new KeypadStartCfm(req.GetSeq(), ERROR_SUCCESS);
QF::PUBLISH(evt, me);
status = Q_TRAN(&AOKeypad::Started);
break;
}
default: {
status = Q_SUPER(&AOKeypad::Root);
break;
}
}
return status;
}
QState AOKeypad::Started(AOKeypad * const me, QEvt const * const e) {
QState status;
switch (e->sig) {
case Q_ENTRY_SIG: {
LOG_EVENT(e);
me->m_syncTimer.armX(CONFIG_KEYPAD_SYNC_INTERVAL, CONFIG_KEYPAD_SYNC_INTERVAL);
status = Q_HANDLED();
break;
}
case Q_EXIT_SIG: {
LOG_EVENT(e);
me->m_syncTimer.disarm();
status = Q_HANDLED();
break;
}
case KEYPAD_STOP_REQ: {
LOG_EVENT(e);
Evt const &req = EVT_CAST(*e);
Evt *evt = new KeypadStopCfm(req.GetSeq(), ERROR_SUCCESS);
QF::PUBLISH(evt, me);
status = Q_TRAN(AOKeypad::Stopped);
break;
}
case KEYPAD_SYNC: {
//LOG_EVENT(e);
keyState *ks;
uint32_t in;
bool val;
keyEvent keyevent;
for(int i=0; i<KEYPAD_MAX_ROWS; i++){
if((1 << i) & KEYPAD_ACTIVE_ROWS){
//set the row high
gpio_outset_bulk(PORTA, keypad_output_masks[i]);
//short delay
for(int __tmr = 0; __tmr<100; __tmr++) __asm__ volatile ("NOP;");
in = gpio_read_bulk() & KEYPAD_INPUT_MASK; //read everything at once
for(int j=0; j<KEYPAD_MAX_COLS; j++){
if((1 << j) & KEYPAD_ACTIVE_COLS){
val = (in & keypad_input_masks[j]) > 0;
keyevent.bit.NUM = KEYPAD_EVENT(i, j);
ks = &me->m_state[keyevent.bit.NUM];
if(ks->bit.ACTIVE & KEYPAD_HIGH && val){
keyevent.bit.EDGE = KEYPAD_EDGE_HIGH;
me->m_fifo->Write(&keyevent.reg, 1);
}
if(ks->bit.ACTIVE & KEYPAD_LOW && !val){
keyevent.bit.EDGE = KEYPAD_EDGE_LOW;
me->m_fifo->Write(&keyevent.reg, 1);
}
if(ks->bit.ACTIVE & KEYPAD_RISING && !ks->bit.STATE && val){
keyevent.bit.EDGE = KEYPAD_EDGE_RISING;
me->m_fifo->Write(&keyevent.reg, 1);
}
if(ks->bit.ACTIVE & KEYPAD_FALLING && ks->bit.STATE && !val){
keyevent.bit.EDGE = KEYPAD_EDGE_FALLING;
me->m_fifo->Write(&keyevent.reg, 1);
}
ks->bit.STATE = val;
}
}
//set the row back low
gpio_outclr_bulk(PORTA, keypad_output_masks[i]);
}
}
//create an interrupt if there are events in the FIFO
if(me->m_fifo->GetUsedCount() > 0){
me->m_status.bit.DATA_RDY = 1;
if(me->m_inten.bit.DATA_RDY){
//post an interrupt event
Evt *evt = new InterruptSetReq( SEESAW_INTERRUPT_KEYPAD_DATA_RDY );
QF::PUBLISH(evt, me);
}
}
status = Q_HANDLED();
break;
}
case KEYPAD_READ_REG_REQ: {
LOG_EVENT(e);
KeypadReadRegReq const &req = static_cast<KeypadReadRegReq const &>(*e);
Fifo *dest = req.getDest();
uint8_t reg = req.getReg();
uint8_t c = 0;
Evt *evt;
if(reg == SEESAW_KEYPAD_FIFO){
me->m_status.bit.DATA_RDY = 0;
if(me->m_inten.bit.DATA_RDY){
//post an interrupt event
evt = new InterruptClearReq( SEESAW_INTERRUPT_KEYPAD_DATA_RDY );
QF::PUBLISH(evt, me);
}
//give the requester our pipe
evt = new DelegateDataReady(req.getRequesterId(), me->m_fifo);
}
else{
switch (reg){
case SEESAW_KEYPAD_STATUS:
c = me->m_status.reg;
break;
case SEESAW_KEYPAD_COUNT:
c = me->m_fifo->GetUsedCount();
break;
default:
break;
}
//return the read register in the default fifo
evt = new DelegateDataReady(req.getRequesterId());
dest->Write(&c, 1);
}
QF::PUBLISH(evt, me);
status = Q_HANDLED();
break;
}
case KEYPAD_WRITE_REG_REQ: {
LOG_EVENT(e);
KeypadWriteRegReq const &req = static_cast<KeypadWriteRegReq const &>(*e);
uint32_t c = req.getValue();
switch (req.getReg()){
case SEESAW_KEYPAD_INTENSET:
me->m_inten.reg |= c >> 8;
break;
case SEESAW_KEYPAD_INTENCLR:
me->m_inten.reg &= ~(c >> 8);
break;
case SEESAW_KEYPAD_EVENT:
//turn an event on or off
keyState *ks;
ks = &me->m_state[(c >> 8) & 0xFF];
if(c & 0x01) //activate the selected edges
ks->bit.ACTIVE |= (c >> 1);
else //deactivate the selected edges
ks->bit.ACTIVE &= (c >> 1);
break;
default:
break;
}
status = Q_HANDLED();
break;
}
default: {
status = Q_SUPER(&AOKeypad::Root);
break;
}
}
return status;
}