I want to capture mouse movement packets in an embedded system receiving packets of data from an input mouse (initialized in ps/2 mode). How do I get my exception handler to get called? The interrupt handler is never triggered. I set up my processor to run with external interrupts and connected only one signal to the processor's interrupt input port, and I do not use an interrupt controller. To capture input movements, I use a software based circular buffer.
Please help.
#include "xparameters.h"
#include "xil_exception.h"
#include "ps2_core.h"
Ps2Core::Ps2Core(uint32_t core_base_addr) {
base_addr = core_base_addr;
registerCallback();
}
Ps2Core::~Ps2Core() {
}
void Ps2Core::enqueue(unsigned char value) {
if ((tail + 1) % QUEUE_SIZE == head) {
// queue is full, do nothing
return;
}
queue[tail] = value;
queueCount++;
tail = (tail + 1) % QUEUE_SIZE;
}
unsigned char Ps2Core::dequeue(void) {
if (head == tail) {
// queue is empty, do nothing
return 0;
}
unsigned char value = queue[head];
queueCount--;
head = (head + 1) % QUEUE_SIZE;
return value;
}
void Ps2Core::handleInterrupt(Ps2Core *ps2) {
uint8_t byte;
byte = ps2->rx_byte();
ps2->enqueue(byte);
}
void Ps2Core::registerCallback(){
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)handleInterrupt,
this);
Xil_ExceptionEnable();
}
int Ps2Core::get_mouse_activity(int *lbtn, int *rbtn, int *xmov,
int *ymov, int *zmov) {
uint8_t b1, b2, b3, b4;
uint32_t tmp;
/* retrieve bytes only if 4 or a multiple of 4 exist in queue */
if (queueCount >= 4) {
b1 = dequeue();
b2 = dequeue();
b3 = dequeue();
b4 = dequeue();
}
else
return (0);
/* extract button info */
*lbtn = (int) (b1 & 0x01); // extract bit 0
*rbtn = (int) (b1 & 0x02) >> 1; // extract bit 1
/* extract x movement; manually convert 9-bit 2's comp to int */
tmp = (uint32_t) b2;
if (b1 & 0x10) // check MSB (sign bit) of x movement
tmp = tmp | 0xffffff00; // manual sign-extension if negative
*xmov = (int) tmp; // data conversion
/* extract y movement; manually convert 9-bit 2's comp to int */
tmp = (uint32_t) b3;
if (b1 & 0x20) // check MSB (sign bit) of y movement
tmp = tmp | 0xffffff00; // manual sign-extension if negative
*ymov = (int) tmp; // data conversion
tmp = (uint32_t) b4;
if (b4 & 0x08) // check MSB (sign bit) of z movement
tmp = tmp | 0xfffffff0; // manual sign-extension if negative
*zmov = (int) tmp; // data conversion
/* success */
return (1);
}
Module Instances
//instantiate uBlaze MCS
cpu cpu_unit (
.Clk(clk_100M),
.Reset(reset),
.IO_addr_strobe(io_addr_strobe),
.IO_address(io_address),
.IO_byte_enable(io_byte_enable),
.IO_read_data(io_read_data),
.IO_read_strobe(io_read_strobe),
.IO_ready(io_ready),
.IO_write_data(io_write_data),
.IO_write_strobe(io_write_strobe),
.INTC_Interrupt(interrupt)
);
// instantiated i/o subsystem
mmio_sys_sampler_arty_a7 mmio_unit (
.clk(clk_100M),
.reset(reset),
.mmio_cs(fp_mmio_cs),
.mmio_wr(fp_wr),
.mmio_rd(fp_rd),
.mmio_addr(fp_addr),
.mmio_wr_data(fp_wr_data),
.mmio_rd_data(fp_rd_data),
.rx_done_tick(interrupt),
.ps2d_in(ps2d_in),
.ps2c_in(ps2c_in),
.tri_c(tri_c),
.tri_d(tri_d),
.ps2c_out(ps2c_out),
.ps2d_out(ps2d_out),
.rx(rx),
.tx(tx)
);
EDIT - Latest Code (Interrupt not triggered, but interrupt signal generated on each byte received, verified by visual output on oscilloscope)
#define INTERRUPT_ID 16U //if != 16U then MICROBLAZE_EXCEPTIONS_ENABLED must be defined
// Define the address of the interrupt control register for the MicroBlaze processor
#define INTERRUPT_CONTROL_REG XPAR_IOMODULE_0_IO_BASEADDR + XIN_ISR_OFFSET
void Ps2Core::handleInterrupt(Ps2Core *ps2) {
uint8_t byte;
byte = ps2->rx_byte();
ps2->enqueue(byte);
}
void Ps2Core::clearInterrupt(unsigned int interruptID) {
// Clear the interrupt flag by writing a 1 to the appropriate bit in the ISR
*(volatile unsigned int*)(INTERRUPT_CONTROL_REG) = (1 << interruptID);
}
void Ps2Core::checkInterruptStatus(Ps2Core *ps2) {
/* Read the status of the interrupt */
u32 IntrStatus = XIOModule_DiscreteRead(&ps2->intr, INTERRUPT_CONTROL_REG);
if (IntrStatus) {
Ps2Core::handleInterrupt(ps2);
Ps2Core::clearInterrupt(INTERRUPT_ID);
}
}
void Ps2Core::setUpInterrupt(){
Xil_ExceptionInit();
// Register the interrupt handler function with the microblaze processor
//microblaze_register_handler((XInterruptHandler)checkInterruptStatus, this);
// Register interrupt handler function
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)checkInterruptStatus, this);
}
Judging by the port names, it looks like you have an interrupt controller in your 'cpu' module. But I don't see any code configuring that--instead it's connecting directly to the MicroBlaze CPU's exception handler. Xilinx's driver for the interrupt controller should be attaching itself to the exception handler and your code should be connecting to it via register_int_handler / enable_interrupt.
Related
I code in ic stm32f103vet for press button to turn on/off the led
#include "stm32f10x_rcc.h"
#include "stm32f10x.h"
#include "misc.h"
void GPIO_Config(void);
BitAction BitVal;
int main(){
GPIO_Config();
GPIO_SetBits(GPIOA, GPIO_Pin_6);
while(1)
{
if(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10)){
while(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10)){
GPIO_WriteBit(GPIOA, GPIO_Pin_6, BitVal);
BitVal =! BitVal; //error: assigning to 'BitAction' from incompatible type 'bool'
}
}
}
}
void GPIO_Config(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE);
/*config Led PA6*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*config Button E10*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure); //button U6
}
with definition of 'BitVal' is
/**
* #brief Bit_SET and Bit_RESET enumeration
*/
typedef enum
{ Bit_RESET = 0,
Bit_SET
}BitAction;
i try to toggle bit value of BitVal with many way but it still not work
how can i do that?
In first, you need to know that below code will toggle your LED from ON to OFF state until button is pressed:
while(1) {
if(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10)){
while(!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10)){
GPIO_WriteBit(GPIOA, GPIO_Pin_6, BitVal);
BitVal =! BitVal; //error: assigning to 'BitAction' from incompatible type 'bool'
}
}
}
It would be good to implement any flag informing that button was already pressed and some mechanism to handle button connector debounce effect.
If you want to learn how to toggle bit values in C (which is used many times a specially in embedded programming) try to get familiar with XOR (^) operations.
because your enum have only two values 0 or 1 and to have button state flag and also button debounce effect handled properly, you can use:
int main() {
GPIO_Config();
GPIO_SetBits(GPIOA, GPIO_Pin_6);
int debounceCnt = 0;
_Bool btnPressed = false;
while (1) {
while (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10) && !btnPressed) {
if (80 < debounceCnt++) {
GPIO_WriteBit(GPIOA, GPIO_Pin_6, BitVal);
BitVal ^= Bit_SET; // toggle BitVal
btnPressed = true;
debounceCnt = 0;
}
}
if (btnPressed && GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10)) {
if (80 < ++debounceCnt) {
btnPressed = false;
debounceCnt = 0;
}
}
}
}
I'm trying to build a tachometer in C++ for my ESP32. When I uncomment Serial.printf("outside rev: %d \n", rev); outside of the conditional it works, but when I comment it I get values that are orders of magnitude greater than they should be (700 revolutions without, vs 7 revolutions with). My best guess is that the print statement is slowing the loop() down just enough for incrementRevolutions() to toggle the global variable passedMagnet from true to false before the next loop. That would make sense, since a delay in updating passedMagnet would allow newRevCount++; to be triggered multiple times. But this is obviously something I can't debug with either print statements or step-through debugging given the time-sensitive nature of the race condition.
bool passedMagnet = true;
int incrementRevolutions(int runningRevCount, bool passingMagnet)
{
// Serial.printf("passedMagnet: %d , passingMagnet %d , runningRevCount: %d \n", passedMagnet, passingMagnet, runningRevCount);
int newRevCount = runningRevCount;
if (passedMagnet && passingMagnet)
{ //Started a new pass of the magnet
passedMagnet = false;
newRevCount++;
}
else if (!passedMagnet && !passingMagnet)
{ //The new pass of the magnet is complete
passedMagnet = true;
}
return newRevCount;
}
unsigned long elapsedTime = 0;
unsigned long intervalTime = 0;
int rev = 0;
void loop()
{
intervalTime = millis() - elapsedTime;
rev = incrementRevolutions(rev, digitalRead(digitalPin));
// Serial.printf("outside rev: %d \n", rev);
if (intervalTime > 1000)
{
Serial.printf("rev: %d \n", rev);
rev = 0;
elapsedTime = millis();
}
}
Is this a known gotcha with Arduino or C++ programming? What should I do to fix it?
I think the test is to blame. I had to rename and move things a bit to visualize the logic, sorry about that.
bool magStateOld = false; // initialize to digitalRead(digitalPin) in setup()
int incrementRevolutions(int runningRevCount, bool magState)
{
int newRevCount = runningRevCount;
// detect positive edge.
if (magState && !magStateOld) // <- was eq. to if (magState && magStateOld)
// the large counts came from here.
{
newRevCount++;
}
magStateOld = magState; // record last state unconditionally
return newRevCount;
}
You could also write it as...
int incrementRevolutions(int n, bool magState)
{
n += (magState && !magStateOld);
magStateOld = magState;
return n;
}
But the most economical (and fastest) way of doing what you want would be:
bool magStateOld;
inline bool positiveEdge(bool state, bool& oldState)
{
bool result = (state && !oldState);
oldState = state;
return result;
}
void setup()
{
// ...
magStateOld = digitalRead(digitalPin);
}
void loop()
{
// ...
rev += (int)positiveEdge(digitalRead(digitalPin), magStateOld);
// ...
}
It's reusable, and saves both stack space and unnecessary assignments.
If you cannot get clean transitions from your sensor (noise on positive and negative edges, you'll need to debounce the signal a bit, using a timer.
Example:
constexpr byte debounce_delay = 50; // ms, you may want to play with
// this value, smaller is better.
// but must be high enough to
// avoid issues on expected
// RPM range.
// 50 ms is on the high side.
byte debounce_timestamp; // byte is large enough for delays
// up to 255ms.
// ...
void loop()
{
// ...
byte now = (byte)millis();
if (now - debounce_timestamp >= debounce_delay)
{
debounce_timestamp = now;
rev += (int)positiveEdge(digitalRead(digitalPin), magStateOld);
}
// ...
}
I'm currently trying to process the absolute value of a drawing tablet's touch ring, through the Wintab API. However, despite following instructions as they are described in the documentation, it seems like the WTOpen call doesn't set any extension settings. Using the touch ring after initializing Wintab still triggers the default events, while the default events for pen inputs are suppressed and all pen inputs related to my application instead.
Here are the relevant segments of code:
...
#include "wintab.h"
#define PACKETDATA (PK_X | PK_Y | PK_Z | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_TIME | PK_BUTTONS)
#define PACKETMODE 0
#define PACKETTOUCHSTRIP PKEXT_ABSOLUTE
#define PACKETTOUCHRING PKEXT_ABSOLUTE
#include "pktdef.h"
...
internal b32
InitWinTab(HWND Window, window_mapping *Map)
{
if(!LoadWintabFunctions())
return false;
LOGCONTEXT Tablet;
AXIS TabletX, TabletY, TabletZ, Pressure;
if(!gpWTInfoA(WTI_DEFCONTEXT, 0, &Tablet))
return false;
gpWTInfoA(WTI_DEVICES, DVC_X, &TabletX);
gpWTInfoA(WTI_DEVICES, DVC_Y, &TabletY);
gpWTInfoA(WTI_DEVICES, DVC_Z, &TabletZ);
gpWTInfoA(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
UINT TouchStripOffset = 0xFFFF;
UINT TouchRingOffset = 0xFFFF;
for(UINT i = 0, ScanTag = 0; gpWTInfoA(WTI_EXTENSIONS + i, EXT_TAG, &ScanTag); i++)
{
if (ScanTag == WTX_TOUCHSTRIP)
TouchStripOffset = i;
if (ScanTag == WTX_TOUCHRING)
TouchRingOffset = i;
}
Tablet.lcOptions |= CXO_MESSAGES;
Tablet.lcPktData = PACKETDATA;
Tablet.lcPktMode = PACKETMODE;
Tablet.lcMoveMask = PACKETDATA;
Tablet.lcBtnUpMask = Tablet.lcBtnDnMask;
Tablet.lcInOrgX = 0;
Tablet.lcInOrgY = 0;
Tablet.lcInExtX = TabletX.axMax;
Tablet.lcInExtY = TabletY.axMax;
if(TouchStripOffset != 0xFFFF)
{
WTPKT DataMask;
gpWTInfoA(WTI_EXTENSIONS + TouchStripOffset, EXT_MASK, &DataMask);
Tablet.lcPktData |= DataMask;
}
if(TouchRingOffset != 0xFFFF)
{
WTPKT DataMask;
gpWTInfoA(WTI_EXTENSIONS + TouchRingOffset, EXT_MASK, &DataMask);
Tablet.lcPktData |= DataMask;
}
Map->AxisMax.x = (r32)TabletX.axMax;
Map->AxisMax.y = (r32)TabletY.axMax;
Map->AxisMax.z = (r32)TabletZ.axMax;
Map->PressureMax = (r32)Pressure.axMax;
if(!gpWTOpenA(Window, &Tablet, TRUE))
return false;
return(TabletX.axMax && TabletY.axMax && TabletZ.axMax && Pressure.axMax);
}
...
case WT_PACKET:
{
PACKET Packet;
if(gpWTPacket((HCTX)LParam, (UINT)WParam, &Packet))
{
...
}
} break;
case WT_PACKETEXT:
{
PACKETEXT Packet;
if(gpWTPacket((HCTX)LParam, (UINT)WParam, &Packet))
{
...
}
} break;
...
The bitmask for the packet data in the initialization have sensible bits set for both extensions and don't overlap with the existing bitmask. No stage of the initialization fails. WT_PACKET gets called only with valid packet data while WT_PACKETEXT never gets called. Furthermore, calling WTPacketsGet with a PACKETEXT pointer on the HCTX returned by WTOpen fills the packet with garbage from the regular packet queue. This leaves me with the conclusion that somehow WTOpen didn't receive notification that the extensions should be loaded and I'm unable to find what else I should define in the LOGCONTEXT data structure to change that.
Is there a mistake in my initialization? Or is there a way to get a better readout to why the extensions didn't load?
It turned out that a driver setting prevented the extension packets from being sent, in favor of using the touch ring for different function. Changing this setting resolved the issue. The code didn't contain any errors itself.
I'm trying to use a pointer in a function argument to read a variable from a class, like this:
void Function(int (*Argument)) {
Variable = Object*Argument.ClassVariable;
}
Note: the reason I want to do this is so that I can set the value of a rotary encoder position equal to the value a class variable was previously at, prior to allowing the user to change that class variable value. This way, when the user cycles through the class variables to set them, the starting position for each variable is the saved value.
Class objects are named Object1, Object2, etc.
The variable I want to read from each object is named ClassVariable.
When I call the function, i will useFunction(ClassNumber);
If ClassNumber is 13 at the time the function is called, I want Variable to be made equal to Object13.ClassVariable
Is this possible?
Further Info:
The entire class is public.
Constructor for class objects (note, this is arduino, byte is 8-bit unsigned):
Modes(byte hue, byte sat, byte val, int del, bool hROal, bool hset, bool sset, bool vset, bool dset) {
hueVal = hue;
satVal = sat;
valVal = val;
delVal = del;
hueROallow = hROal;
hSet = hset;
sSet = sset;
vSet = vset;
dSet = dset;
}
Each class object is a separate 'mode' that the user can select. The Argument i intend using in the function is the 'mode number' currently selected.
FULL CODE (very much a work in progress)
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
const byte Button2 = 8;
const byte EncApin = 3; // EncApin **MUST BE interrupt**
const byte EncBpin = 4; // These are the three pins the encoder A/B & button are connected to
const byte EncButt = 2; //
static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; // array to set encoder H/L sequence
byte encVal; // initialises reading from encoder
Adafruit_SH1106 display(0); // Initialise display? (On SPI OLED screens this sets the MOSI/CLK/DC/RST/CS pins)
int displayVal; // to store the current output value for display
String displayMode;
byte mode = 1; // set mode 0 at beginning
byte qtyModes = 3; // set number of modes that may be selected here
byte setMode = 1;
byte qtySetModes = 4; // set number of sub-modes for setting values
///// FUNCTION PROTOTYPES ///////// must be declared here to allow the class to use them
void EncReading();
void EncReadingRO();
////////////////////////////////////////////////////////
////////////// ******** CLASSES ********* //////////////
////////////////////////////////////////////////////////
class Modes {
public: // everything in the class below this will be available to the program outside of the class
// Class Member Variables
// These are initialized at startup
byte hueVal;
byte satVal;
byte valVal;
int delVal;
bool hueROallow;
bool hSet;
bool sSet;
bool vSet;
bool dSet;
byte CLdisplayVal;
String displayDesc;
// These maintain the current state
unsigned long prevMillis; // will store last time LED was updated
// Constructor - creates a Mode & initializes the member variables and state
// additional options for whether rollover of values allowed needed
Modes(byte hue, byte sat, byte val, int del, bool hROal, bool hset, bool sset, bool vset, bool dset) {
hueVal = hue;
satVal = sat;
valVal = val;
delVal = del;
hueROallow = hROal;
hSet = hset;
sSet = sset;
vSet = vset;
dSet = dset;
}
void Update() {
switch (setMode) {
case 1: // case 1 for hue update
if (hSet == 0) {
displayDesc = F(" HUE (FIXED)");
CLdisplayVal = hueVal;
break;
}
if (hueROallow == 1) EncReadingRO();
else EncReading();
hueVal = encVal;
CLdisplayVal = encVal;
displayDesc = F(" HUE");
break;
case 2: // saturation update
if (sSet == 0) {
displayDesc = F(" SATURATION (FIXED)");
CLdisplayVal = satVal;
break;
}
EncReading();
satVal = encVal;
CLdisplayVal = encVal;
displayDesc = F(" SATURATION");
break;
case 3: // value update
if (vSet == 0) {
displayDesc = F(" BRIGHTNESS (FIXED)");
CLdisplayVal = valVal;
break;
}
EncReading();
valVal = encVal;
CLdisplayVal = encVal;
displayDesc = F(" BRIGHTNESS");
break;
case 4: // delay update
if (dSet == 0) {
displayDesc = F(" TIMING (FIXED)");
CLdisplayVal = delVal;
break;
}
EncReading();
delVal = encVal;
CLdisplayVal = encVal;
displayDesc = F(" TIMING");
break;
}
displayReading();
}
void displayReading() {
unsigned long currMillis = millis(); // These four lines are to
static unsigned long prevMillis; // act as a delay, except
if (currMillis - prevMillis >= 100) { // without holding the execution // note: encoder reading sensitive to delal changes, 100 not bad
prevMillis = currMillis; // of other code
display.fillRect(39, 30, 54, 24, BLACK);
display.fillRect(0, 0, 128, 18, WHITE);
display.setTextSize(1);
display.setTextColor(BLACK);
display.setCursor(1,1);
display.println(displayMode);
display.setCursor(1,10);
display.println(displayDesc);
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(39,30);
display.println(CLdisplayVal);
display.display();
}
}
};
////////// Construct objects - this sets up the objects we want of the class 'Modes'
// Modes modex(Hue0-255, Sat0-255, Val0-255, Del0-255, Hue rollover0/1, Hue settable0/1, Sset0/1, Vset0/1, Dse0/1
Modes mode1(50, 100, 150, 100, 1, 1, 1, 1, 1); // object 'mode1', initializing with H50, S100, V150, D100, hue rollover & settable options ON
Modes mode2(55, 105, 155, 105, 0, 1, 1, 1, 1);
Modes mode3(63, 73, 83, 93, 0, 1, 1, 1, 1);
////////////////////////////////////////////////////////
////////// ******** SETUP / LOOP ********* /////////////
////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
display.begin(SH1106_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
display.clearDisplay();
display.display();
pinMode(EncApin, INPUT_PULLUP); // Turn on internal pullup resistors for encoder pins & buttons
pinMode(EncBpin, INPUT_PULLUP);
pinMode(Button2, INPUT_PULLUP);
pinMode(EncButt, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(EncApin), read_encoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(EncButt), encButtPress, FALLING);
pciSetup(Button2); // set up encApin for pin change interrupt
encVal = mode1.hueVal; // initialize the encoder to the mode1 setmode1 value
}
void loop() {
switch(mode) {
case 1:
displayMode = F("MODE:Call it anything");
mode1.Update();
// add call to the mode 1 LED display function here, using mode1.xyz variables
break;
case 2:
displayMode = F("MODE:Second");
mode2.Update();
// add call to the mode 2 LED display function here, using mode2.xyz variables
break;
case 3:
displayMode = F("MODE:Third");
mode3.Update();
// add call to the mode 2 LED display function here, using mode2.xyz variables
break;
}
Serial.print(F("Enc: "));
Serial.print(encVal);
Serial.print(F(" M1 H: "));
Serial.print(mode1.hueVal);
Serial.print(F(" S: "));
Serial.print(mode1.satVal);
Serial.print(F(" V: "));
Serial.print(mode1.valVal);
Serial.print(F(" D: "));
Serial.print(mode1.delVal);
Serial.print(F(" M2 H: "));
Serial.print(mode2.hueVal);
Serial.print(F(" S: "));
Serial.print(mode2.satVal);
Serial.print(F(" V: "));
Serial.print(mode2.valVal);
Serial.print(F(" D: "));
Serial.println(mode2.delVal);
// Serial.print(F("freeMemory()="));
// Serial.println(freeMemory());
}
////////////////////////////////////////////////////////
//////////// ******** FUNCTIONS ********* //////////////
////////////////////////////////////////////////////////
///// Function to set encVal, 0-255, NO rollover
void EncReading() {
int8_t encoderdata;
encoderdata = read_encoder(); // returns the +- value from the read_encoder function
if (encoderdata) { // if not equal to zero
if (encVal+encoderdata>255 || encVal+encoderdata<0); // these if/else statements clamp encVal to prevent byte rollover
else {
encVal += encoderdata;
} // end else
}
}
///// Function to set encVal, 0-255, WITH rollover
void EncReadingRO() {
int8_t encoderdata;
encoderdata = read_encoder(); // returns the +- value from the read_encoder function
if (encoderdata) { // if not equal to zero
encVal += encoderdata;
}
}
////////////////////////////////////////////////////////
//////////// ******** INTERRUPTS ********* /////////////
////////////////////////////////////////////////////////
/////// Pin Change Interrupt Setup Function /////////// called in void setup to set the selected pin as a PCI
void pciSetup(byte pin)
{
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
//////////// PCI Interrupt Sub Routines////////
// ISR (PCINT0_vect) is ISR for D8-13 // ISR (PCINT2_vect) is ISR for A0-5 // ISR (PCINT2_vect) is ISR for D0-7 //
ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here, depending on which are set in void setup
{
if(digitalRead(Button2) == LOW)
buttonPress();
}
//////// Func for ENCODER button press ISR ****** CHANGE 'setMode' ***** ///////////////
void encButtPress() {
static unsigned long prevInterrTime = 0;
unsigned long interrTime = millis();
if (interrTime - prevInterrTime > 200) {
setMode++;
// *** ADD HERE *** need to set encVal to whatever the existing value is in the setMode being switched to
if (setMode > qtySetModes) setMode = 1;
prevInterrTime = interrTime;
setPrevEncVal(mode);
}
}
void setPrevEncVal(byte (*modeNum)) {
switch (setMode) {
case 1: encVal = mode&modeNum.hueVal; break;
case 2: encVal = mode1.satVal; break;
case 3: encVal = mode1.valVal; break;
case 4: encVal = mode1.delVal; break;
}
}
//////// Func for button press ISR ****** CHANGE 'mode' ***** ///////////////
void buttonPress() {
static unsigned long prevInterrTime = 0;
unsigned long interrTime = millis();
if (interrTime - prevInterrTime > 200) {
mode++; // increment 'mode'
setMode = 1; // reset 'setMode'
}
if (mode > qtyModes) mode = 1;
prevInterrTime = interrTime;
}
//////// Func for +1/-1 encoder reading ISR ///////
/* returns change in encoder state (-1,0,1) */
int8_t read_encoder() {
static uint8_t old_AB = 0;
old_AB <<= 2;
old_AB |= ((digitalRead(EncBpin))?(1<<1):0) | ((digitalRead(EncApin))?(1<<0):0);
return ( enc_states[( old_AB & 0x0f )]);
}
To answer the question:
You can't, because C++ is a compiled language, and as such, the names of the functions, objects and variables become meaningless (instead, they become just addresses)
For example, you may see in your code
int myVariable = 6;
but the compiler sees:
mov [6], [0x6874]
Read Compiled vs. Interpreted Languages for more reference.
[For your particular case]
You'll be better off if you use interfaces and a factory.
class Mode {
virtual int getAttribute() = 0;
}
class AMeaningfulName : public Mode {
int getAttribute() { return 1; }
}
class SelectAnotherMeaningfulNamePlease : public Mode {
int getAttribute() { return 2; }
}
class ModeFactory {
Mode getMode(int userSelectedMode) {
if (userSelectedMode == MODE_1) {
return new AMeaningfulMode();
} else if (userSelectedMode == MODE_2) {
return new SelectAnotherMeaningulNamePlease();
} else {
//send an exception so the user knows he selected something invalid
}
}
}
///now, in your main or elsewhere
ModeFactory factory = new ModeFactory();
Mode mode = factory.getMode(userSelectedValue);
int theAtttributeIWanted = mode.getAttribute();
Take a look at virtual functions
https://en.wikipedia.org/wiki/Virtual_function
And also at factory pattern
https://www.tutorialspoint.com/design_pattern/factory_pattern.htm
I'm trying out The Arduino ultimate GPS breakout, where I want to get the Longitude and Latitude from the GPS. Then I want to send those two variables wireless via RF. like in the image below:
I use a library for the RF-module named panstamp to be able to send the Longitude and Latitude from Arduino 1, and receiving them in Arduino 2. like in the code below:
Transmitting:
void send_data() {
CCPACKET data;
data.length=2;
float lon=26.533255;
float lat=27.533463;
data.data[0]=lon;
data.data[1]=lat;
if(cc1101.sendData(data)){
Serial.println(data.data[0]);
Serial.println(data.data[1]);
Serial.println(" sent ok ");
return true;
}else{
Serial.println("sent failed ");
return false;
}
}
Receiving:
void loop(){
float j = 0;
lon = packet.data[j];
Serial.print(lon);
Serial.print(" ");
float k = 1;
lat = packet.data[k];
Serial.print(lat);
Serial.println(".");
}
It works perfectly when transmitting and receiving :)
The problem is when I receive those two variables I just receive lon 26.00 and lat 27.00 but not lon 26.533255 lat 27.533463 as I expected.
There are some bugs with the data type I assume. I investigated the panstamp library to find something to change the type but without success.
Here is the header file for CCPACKET:
#ifndef _CCPACKET_H
#define _CCPACKET_H
#include "Arduino.h"
/**
* Buffer and data lengths
*/
#define CC1101_BUFFER_LEN 64
#define CC1101_DATA_LEN CC1101_BUFFER_LEN - 3
/**
* Class: CCPACKET
*
* Description:
* CC1101 data packet class
*/
class CCPACKET
{
public:
/**
* Data length
*/
byte length;
/**
* Data buffer
*/
byte data[CC1101_DATA_LEN];
/**
* CRC OK flag
*/
boolean crc_ok;
/**
* Received Strength Signal Indication
*/
byte rssi;
/**
* Link Quality Index
*/
byte lqi;
};
#endif
and the source code for send data/ receive data:
boolean CC1101::sendData(CCPACKET packet)
{
byte marcState;
bool res = false;
// Declare to be in Tx state. This will avoid receiving packets whilst
// transmitting
rfState = RFSTATE_TX;
// Enter RX state
setRxState();
// Check that the RX state has been entered
while (((marcState = readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x0D)
{
if (marcState == 0x11) // RX_OVERFLOW
flushRxFifo(); // flush receive queue
}
delayMicroseconds(500);
// Set data length at the first position of the TX FIFO
writeReg(CC1101_TXFIFO, packet.length);
// Write data into the TX FIFO
writeBurstReg(CC1101_TXFIFO, packet.data, packet.length);
// CCA enabled: will enter TX state only if the channel is clear
setTxState();
// Check that TX state is being entered (state = RXTX_SETTLING)
marcState = readStatusReg(CC1101_MARCSTATE) & 0x1F;
if((marcState != 0x13) && (marcState != 0x14) && (marcState != 0x15))
{
setIdleState(); // Enter IDLE state
flushTxFifo(); // Flush Tx FIFO
setRxState(); // Back to RX state
// Declare to be in Rx state
rfState = RFSTATE_RX;
return false;
}
// Wait for the sync word to be transmitted
wait_GDO0_high();
// Wait until the end of the packet transmission
wait_GDO0_low();
// Check that the TX FIFO is empty
if((readStatusReg(CC1101_TXBYTES) & 0x7F) == 0)
res = true;
setIdleState(); // Enter IDLE state
flushTxFifo(); // Flush Tx FIFO
// Enter back into RX state
setRxState();
// Declare to be in Rx state
rfState = RFSTATE_RX;
return res;
}
byte CC1101::receiveData(CCPACKET * packet)
{
byte val;
byte rxBytes = readStatusReg(CC1101_RXBYTES);
// Any byte waiting to be read and no overflow?
if (rxBytes & 0x7F && !(rxBytes & 0x80))
{
// Read data length
packet->length = readConfigReg(CC1101_RXFIFO);
// If packet is too long
if (packet->length > CC1101_DATA_LEN)
packet->length = 0; // Discard packet
else
{
// Read data packet
readBurstReg(packet->data, CC1101_RXFIFO, packet->length);
// Read RSSI
packet->rssi = readConfigReg(CC1101_RXFIFO);
// Read LQI and CRC_OK
val = readConfigReg(CC1101_RXFIFO);
packet->lqi = val & 0x7F;
packet->crc_ok = bitRead(val, 7);
}
}
else
packet->length = 0;
setIdleState(); // Enter IDLE state
flushRxFifo(); // Flush Rx FIFO
//cmdStrobe(CC1101_SCAL);
// Back to RX state
setRxState();
return packet->length;
}
Please someone help me :)
The link to the Panstamp library: PanStamp Library
As far as I see it, you lost your presicion here:
float lon=26.533255;
float lat=27.533463;
data.data[0]=lon;
data.data[1]=lat;
because data is an array of bytes according to this:
/**
* Data buffer
*/
byte data[CC1101_DATA_LEN];
You need to bufferise data correctly.
float lon=26.533255;
byte *p = (byte *)&lon;
for (int i = 0; i < sizeof(lon); i++){
data.data[i]= p[i];
}
do like this if it works proceed the same with lat or make a function like floattobyte and use.
HighPredator is right!
From the panstamp lib we see that the CCPACKET::data field is a uint8_t array:
https://github.com/panStamp/panstamp/wiki/CCPACKET#data
Basically when you write:
float lon=26.533255;
float lat=27.533463;
data.data[0]=lon;
data.data[1]=lat;
The compiler is essentially doing:
data.data[0]=uint8_t(lon); // So 26.533255f just becomes 26
data.data[1]=uint8_t(lat); // So 27.533463just becomes 27
You need to understand the float type, which is 4-bytes long and so you need to make your packet 8 bytes long and transmit the raw bytes like this:
data.length = 8;
data.data[0] = ((uint8_t*)(&lon))[0]; // Transfer first byte of the float
data.data[1] = ((uint8_t*)(&lon))[1];
data.data[2] = ((uint8_t*)(&lon))[2];
data.data[3] = ((uint8_t*)(&lon))[3]; // Transfer last byte of the float
data.data[4] = ((uint8_t*)(&lat))[0]; // Transfer first byte of the float
data.data[5] = ((uint8_t*)(&lat))[1];
data.data[6] = ((uint8_t*)(&lat))[2];
data.data[7] = ((uint8_t*)(&lat))[3]; // Transfer last byte of the float
On the receiving end, you can recompose the floats like this:
float lon, lat;
((uint8_t*)(&lon))[0] = data.data[0]; // Place first byte
((uint8_t*)(&lon))[1] = data.data[1];
((uint8_t*)(&lon))[2] = data.data[2];
((uint8_t*)(&lon))[3] = data.data[3]; // Place last byte
((uint8_t*)(&lat))[0] = data.data[4]; // Place first byte
((uint8_t*)(&lat))[1] = data.data[5];
((uint8_t*)(&lat))[2] = data.data[6];
((uint8_t*)(&lat))[3] = data.data[7]; // Place last byte
Hope that helps.