My C++ skills are rather amateurish, but I’m trying to write a library for a differential drive robotic platform. The library in question needs to utilise a PID library and a rotary encoder library.
I’d like to utilise “levels of abstraction” to do this, rather than just having a massive “sketch” as it makes things a lot easier to read, and to be honest, is the point of object orientated programming. I.e i can write a class to control the a dc motor and just use multiple instances for each side. (I apologise if I’ve got the terminology wrong).
Essentially I’m having trouble utilising the third party libraries within my library.
If i was to write a simple sketch that used the two third party libraries, it would look like this (paraphrasing) -
#include <Arduino.h>
#include <RotaryEncoder.h>
#include <PID_v1.h>
#include <L298N.h>
char A_IN1 = 8;
char A_IN2 = 9;
char A_EN = 17;
char PIN_IN1 = 4;
char PIN_IN2 = 5;
double Setpoint, Output;
double Input = 0;
double Kp=1.3, Ki=15, Kd=0.01;
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
void checkPosition(){
//some code that increments the encoder value
}
void setup(){
attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, CHANGE);
pinMode(PIN_IN1, INPUT);
attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, CHANGE);
pinMode(PIN_IN2, INPUT);
Setpoint = 0;
myPID.SetSampleTime(20);
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0,255);
}
void loop(){
//loop code that uses PID to drive motors at appropriate speed etc etc
}
How does one go about implementing the two libraries into a custom library?
E.g two files L298N.cpp and L298N.h? Below is what I’ve tried, but no luck.
L298N.cpp
#include "Arduino.h"
#include "L298N.h"
#include <RotaryEncoder.h>
#include <PID_v1.h>
L298N::L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2){
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
pinMode(A_INT1, INPUT);
pinMode(A_INT2, INPUT);
RotaryEncoder encoder(A_INT1, A_INT2, RotaryEncoder::LatchMode::TWO03);
PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
pid.SetSampleTime(20);
pid.SetMode(AUTOMATIC);
pid.SetOutputLimits(0,255);
_IN1 = IN1;
_IN2 = IN2;
_EN = EN;
}
void L298N::checkPosition(){
encoder.tick(); // just call tick() to check the state.
}
double L298N::calculate_rpm(){
long new_position = encoder.getPosition();
long position_change;
double RPM;
if (new_position != old_position) {
tick_time = (millis() - last_time);
position_change = old_position - new_position;
RPM = 1 / ((double(tick_time / position_change) * 125)/1000/60); //10041 18538 = ticks per rev, 1 rev = 42.73cm
old_position = new_position;
last_time = millis();
}
else{
RPM = 0.0;
}
delay(20); // required for dagu as encoders are shit and only pulse 125 times per rev
return RPM;
}
void L298N::set_rpm(int rpm){
Setpoint = rpm;//covert_vel_rpm(vel);
Input = calculate_rpm();
pid.Compute();
if (Setpoint > 0.0){
forwards(char(Output));
}
else{
backwards(char(Output));
}
}
void L298N::forwards(char pwm){
digitalWrite(_IN1, HIGH);
digitalWrite(_IN2, LOW);
analogWrite(_EN, pwm);
return;
}
void L298N::backwards(char pwm){
digitalWrite(_IN1, LOW);
digitalWrite(_IN2, HIGH);
analogWrite(_EN, pwm);
return;
}
L298N.h
#ifndef L298N_h
#define L298N_h
#include "Arduino.h"
#include <RotaryEncoder.h>
#include <PID_v1.h>
class L298N
{
public:
L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2);
void set_rpm(int rpm);
private:
char _IN1, _IN2, _EN, _INT1, _INT2;
double last_time = millis();
double tick_time = 0;
long old_position = 0;
double Setpoint, Output;
double Input = 0;
double Kp=1.3, Ki=15, Kd=0.01;
RotaryEncoder encoder;//(char A_INT1, char A_INT2, RotaryEncoder::LatchMode::TWO03);
PID pid;//(double &Input, double &Output, double &Setpoint, double Kp, double Ki, double Kd, char DIRECT);
void checkPosition();
double calculate_rpm();
void forwards(char pwm);
void backwards(char pwm);
};
#endif
test.ino
#include <L298N.h>
#include <RotaryEncoder.h>
#include <PID_v1.h>
char A_IN1 = 8;
char A_IN2 = 9;
char A_EN = 17;
char A_INT1 = 3;
char A_INT2 = 4;
L298N left(A_IN1, A_IN2, A_EN, A_INT1, A_INT2);
void setup(){
}
void loop(){
left.set_rpm(20);
}
errors:
Arduino: 1.8.15 (Linux), Board: "Arduino Uno"
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp: In constructor
'L298N::L298N(char, char, char, char, char)':
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:7:67: error: no
matching function for call to 'RotaryEncoder::RotaryEncoder()'
L298N::L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2)
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:10:0,
from /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:39:3:
note: candidate: RotaryEncoder::RotaryEncoder(int, int,
RotaryEncoder::LatchMode) RotaryEncoder(int pin1, int pin2,
LatchMode mode = LatchMode::FOUR0); ^~~~~~~~~~~~~
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:39:3:
note: candidate expects 3 arguments, 0 provided
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate: constexpr RotaryEncoder::RotaryEncoder(const
RotaryEncoder&) class RotaryEncoder
^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate: constexpr
RotaryEncoder::RotaryEncoder(RotaryEncoder&&)
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:7:67: error: no
matching function for call to 'PID::PID()' L298N::L298N(char IN1,
char IN2, char EN, char A_INT1, char A_INT2)
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:11:0,
from /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:24:5: note: candidate:
PID::PID(double*, double*, double*, double, double, double, int)
PID(double*, double*, double*, // * constructor. links the PID to the Input, Output, and
^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:24:5: note: candidate expects 7 arguments, 0 provided
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:20:5: note: candidate:
PID::PID(double*, double*, double*, double, double, double, int, int)
PID(double*, double*, double*, // * constructor. links the PID to the Input, Output, and
^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:20:5: note: candidate expects 8 arguments, 0 provided
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate:
constexpr PID::PID(const PID&) class PID
^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate:
constexpr PID::PID(PID&&)
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate
expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:13:70: error:
invalid use of non-static member function 'void
L298N::checkPosition()'
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:0:
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:33:7: note:
declared here void checkPosition();
^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:14:70: error:
invalid use of non-static member function 'void
L298N::checkPosition()'
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:0:
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:33:7: note:
declared here void checkPosition();
^~~~~~~~~~~~~ exit status 1 Error compiling for board Arduino Uno.
This report would have more information with "Show verbose output
during compilation" option enabled in File -> Preferences.
Any help of how to do this properly would be greatly appreciated. It’s just using a class within a class (terminology might be wrong) but this is quite trivial to do in Python.
Cheers!
You have several problems.
You said
PID and RotaryEncoder libraries are standard arduino libraries
That's wrong, those libraries are not standard, you have to install them using the Library Manager (otherwise it would have compiled).
Those are:
- RotaryEncoder, by Mathias Hertel
http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx
- Arduino PID Library, by Brett Beauregard
https://github.com/br3ttb/Arduino-PID-Library
Local includes should use quotes, system/external includes must use brackets:
#include "L298N.h"
#include <RotaryEncoder.h>
RotaryEncoder does not have a default constructor, so you either initialize it using the initializer list or just remove the member variable.
PID doesn't have a default constructor either.
You cannot use a class member function as an interrupt handler. This:
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
will never work. You may have to use a singleton or similar:
L298N left(A_IN1, A_IN2, A_EN, A_INT1, A_INT2);
// code
void int_checkPosition() {
left.checkPosition()
}
// more code
// this may go in setup()
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
I changed the code in the constructor to make it look like this and compiles.
// encoder and pid initialized properly
L298N::L298N(byte IN1, byte IN2, byte mEN, byte A_INT1, byte A_INT2)
: encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03)
, pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT)
{
piL298N::L298N(byte IN1, byte IN2, byte EN, byte A_INT1, byte A_INT2)
: encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03)
, pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT)
{
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EN, OUTPUT);
// this doesn't work (use the recommendation above)
//attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
//attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
pinMode(A_INT1, INPUT);
pinMode(A_INT2, INPUT);
// you don't need this. in fact, it wasn't going to work
// because you needed it to be a class member.
//RotaryEncoder encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03);
//PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
pid.SetSampleTime(20);
pid.SetMode(AUTOMATIC);
pid.SetOutputLimits(0,255);
_IN1 = IN1;
_IN2 = IN2;
_EN = EN;
}
It is up to you to fix the issue with the interrupt handler.
i think I understand what are you asking. Arduino (and many other IDE) has two different types o library
1) Local libraries
2) External Libraries
Local Libraries are those that you include by the syntaxis:
#include "Your_library.h"
External Libraries are those with the syntaxis:
#include <Your_library.h>
The difference between them is that the Local Libraries are supposed to be in the same folder of the proyect, it means that if you have a folder
Arduino/My_proyect/My_Proyect.ino
the library should be in
Arduino/My_proyect/Your_library.h
Instead, external Libraries are in the Arduino "libraries" folder
Arduino/libraries/Your_library/Your_library.h
Maybe the problem with your code is that you are using the bad type of syntaxis for the library, try to put all the libraries you want to use (.h and .cpp) in the same file and create a folder in the arduino "libraries" folder, then, include your librarys using the external syntaxis
#include <Folder_name.h>
This should work!
I am running stretch on raspberry pi 1. Only libcec4 and libcec4-dev are available in repo.
Simple code I found from github is based on old version of libcec.
// Build command:
// g++-4.8 -std=gnu++0x -fPIC -g -Wall -march=armv6 -mfpu=vfp -mfloat-abi=hard -isystem /opt/vc/include/ -isystem /opt/vc/include/interface/vcos/pthreads/ -isystem /opt/vc/include/interface/vmcs_host/linux/ -I/usr/local/include -L /opt/vc/lib -lcec -lbcm_host -ldl cec-simplest.cpp -o cec-simplest
//#CXXFLAGS=-I/usr/local/include
//#LINKFLAGS=-lcec -ldl
#include <libcec/cec.h>
// cecloader.h uses std::cout _without_ including iosfwd or iostream
// Furthermore is uses cout and not std::cout
#include <iostream>
using std::cout;
using std::endl;
#include <libcec/cecloader.h>
#include "bcm_host.h"
//#LINKFLAGS=-lbcm_host
#include <algorithm> // for std::min
// The main loop will just continue until a ctrl-C is received
#include <signal.h>
bool exit_now = false;
void handle_signal(int signal)
{
exit_now = true;
}
//CEC::CBCecKeyPressType
int on_keypress(void* not_used, const CEC::cec_keypress msg)
{
std::string key;
switch( msg.keycode )
{
case CEC::CEC_USER_CONTROL_CODE_SELECT: { key = "select"; break; }
case CEC::CEC_USER_CONTROL_CODE_UP: { key = "up"; break; }
case CEC::CEC_USER_CONTROL_CODE_DOWN: { key = "down"; break; }
case CEC::CEC_USER_CONTROL_CODE_LEFT: { key = "left"; break; }
case CEC::CEC_USER_CONTROL_CODE_RIGHT: { key = "right"; break; }
default: break;
};
std::cout << "on_keypress: " << static_cast<int>(msg.keycode) << " " << key << std::endl;
return 0;
}
int main(int argc, char* argv[])
{
// Install the ctrl-C signal handler
if( SIG_ERR == signal(SIGINT, handle_signal) )
{
std::cerr << "Failed to install the SIGINT signal handler\n";
return 1;
}
// Initialise the graphics pipeline for the raspberry pi. Yes, this is necessary.
bcm_host_init();
// Set up the CEC config and specify the keypress callback function
CEC::ICECCallbacks cec_callbacks;
CEC::libcec_configuration cec_config;
cec_config.Clear();
cec_callbacks.Clear();
const std::string devicename("CECExample");
devicename.copy(cec_config.strDeviceName, std::min(devicename.size(),13u) );
cec_config.clientVersion = CEC::LIBCEC_VERSION_CURRENT;
cec_config.bActivateSource = 0;
cec_config.callbacks = &cec_callbacks;
cec_config.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_RECORDING_DEVICE);
cec_callbacks.CBCecKeyPress = &on_keypress;
// Get a cec adapter by initialising the cec library
CEC::ICECAdapter* cec_adapter = LibCecInitialise(&cec_config);
if( !cec_adapter )
{
std::cerr << "Failed loading libcec.so\n";
return 1;
}
// Try to automatically determine the CEC devices
CEC::cec_adapter devices[10];
int8_t devices_found = cec_adapter->FindAdapters(devices, 10, NULL);
if( devices_found <= 0)
{
std::cerr << "Could not automatically determine the cec adapter devices\n";
UnloadLibCec(cec_adapter);
return 1;
}
// Open a connection to the zeroth CEC device
if( !cec_adapter->Open(devices[0].comm) )
{
std::cerr << "Failed to open the CEC device on port " << devices[0].comm << std::endl;
UnloadLibCec(cec_adapter);
return 1;
}
// Loop until ctrl-C occurs
while( !exit_now )
{
// nothing to do. All happens in the CEC callback on another thread
sleep(1);
}
// Close down and cleanup
cec_adapter->Close();
UnloadLibCec(cec_adapter);
return 0;
}
It does not compile using libcec4 and libcec4-dev and throws these errors ::
cec-simplest.cpp: In function ‘int main(int, char**)’:
cec-simplest.cpp:76:19: error: ‘CEC::ICECCallbacks {aka struct CEC::ICECCallbacks}’ has no member named ‘CBCecKeyPress’; did you mean ‘keyPress’?
cec_callbacks.CBCecKeyPress = &on_keypress;
^~~~~~~~~~~~~
cec-simplest.cpp:88:41: error: ‘class CEC::ICECAdapter’ has no member named ‘FindAdapters’; did you mean ‘PingAdapter’?
int8_t devices_found = cec_adapter->FindAdapters(devices, 10, NULL);
When I renamed CBCecKeyPress to keyPress and FindAdapters to PingAdapter , I got these errors ::
cec-simplest.cpp: In function ‘int main(int, char**)’:
cec-simplest.cpp:76:33: error: invalid conversion from ‘int (*)(void*, CEC::cec_keypress)’ to ‘void (*)(void*, const cec_keypress*) {aka void (*)(void*, const CEC::cec_keypress*)}’ [-fpermissive]
cec_callbacks.keyPress = &on_keypress;
^~~~~~~~~~~~
cec-simplest.cpp:88:70: error: no matching function for call to ‘CEC::ICECAdapter::PingAdapter(CEC::cec_adapter [10], int, NULL)’
int8_t devices_found = cec_adapter->PingAdapter(devices, 10, NULL);
^
In file included from cec-simplest.cpp:5:0:
/usr/include/libcec/cec.h:77:18: note: candidate: virtual bool CEC::ICECAdapter::PingAdapter()
virtual bool PingAdapter(void) = 0;
^~~~~~~~~~~
/usr/include/libcec/cec.h:77:18: note: candidate expects 0 arguments, 3 provided
Some Info that I got about keyPress from /usr/include/libcec/cectypes.h ::
typedef struct cec_keypress
{
cec_user_control_code keycode; /**< the keycode */
unsigned int duration; /**< the duration of the keypress */
} cec_keypress;
typedef struct ICECCallbacks
{
void (CEC_CDECL* keyPress)(void* cbparam, const cec_keypress* key);
/*!
* #brief Transfer a CEC command from libCEC to the client.
* #param cbparam Callback parameter provided when the callbacks were set up
* #param command The command to transfer.
*/
void Clear(void)
{
keyPress = nullptr;
There is no documentation available for libcec.
What modifications do I need to do to make it work with libcec4 ?
I'm the author of the code you are asking about. I know that a year has passed since you asked the question but I only stumbled onto this stackoverflow question because I was searching for the solution myself! LOL. Luckily I've cracked the problem by myself.
I've updated the github code to work https://github.com/DrGeoff/cec_simplest and I've written a blog post about the code https://drgeoffathome.wordpress.com/2018/10/07/a-simple-libcec4-example-for-the-raspberry-pi/
In short, these are the changes I had to make. First up, the on_keypress function now is passed a pointer to a cec_keypress message rather than a by-value copy of a message. The next change is that the CEC framework has changed the name of the callback function from CBCecKeyPress to the simple keyPress. In a similar vein, the FindAdapters function is now DetectAdapters (not PingAdapters as you tried). And finally, the DetectAdapters function fills in an array of cec_adapter_descriptor rather than cec_adapter, which has the flow on effect to the Open call taking a strComName rather than simply comm.
What I am trying to do is start two threads, each running the crit_area function. I need to pass ptrBank from main() to crit_area() so that the BANK struct balance[0] and balance[1] are updated by the threads sequentially. To accomplish this I've created a semaphore class of my own sem_class-1.h. I've gotten through all of the compile errors except for the following:
race1d.cc:49:61: error: invalid conversion from ‘void* (*)(BANK*)’ to ‘void* (*)(void*)’ [-fpermissive]
/usr/include/pthread.h:225:12: error: initializing argument 3 of ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’ [-fpermissive]
race1d.cc:54:61: error: invalid conversion from ‘void* (*)(BANK*)’ to ‘void* (*)(void*)’ [-fpermissive]
/usr/include/pthread.h:225:12: error: initializing argument 3 of ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’ [-fpermissive]
race1d.cc: In function ‘void* crit_area(BANK*)’:
race1d.cc:105:10: error: too few arguments to function ‘void (* signal(int, __sighandler_t))(int)’
/usr/include/signal.h:101:23: note: declared here
I'm not very comfortable with pointers yet (some of this code was given to me to start with), and am not even sure if I can pass a pointer to a structure through pthread to a function. I've tried passing the pointer in various ways in the pthread function call, i.e. pthread_create(&tid1, NULL, crit_area, ptrBank) or pthread_create(&tid1, NULL, crit_area, *ptrBank) all to no avail. I've also spent a few hours online searching for similar problems. Can anyone help? Yes... this is part of a homework assignment, of which my lab partner and I have completed all but this final part. [please don't judge our newb code]
#include <unistd.h> /* Symbolic Constants */
#include <sys/types.h> /* Primitive System Data Types */
#include <sys/wait.h> /* System error numbers */
#include <errno.h> /* Errors */
#include <stdio.h> /* Input/Output */
#include <stdlib.h> /* General Utilities */
#include <pthread.h> /* POSIX Threads */
#include <string.h> /* String handling */
//#include <semaphore.h> /* Semaphore */
//#include <fcntl.h> /* Needed for arguments to sem_open */
#include "shm437.h"
#include "sem_class-1.h"
// BANK Structure, with integer variable 'balance'
struct BANK
{
int balance[2];
// BANK function sets balance variable == 0
BANK()
{
balance[0]=balance[1]=0;
}
};
void * crit_area(BANK * a);
// Begin main program
int main(int argc, char **argv)
{
pthread_t tid1, tid2;
Shm437 *pShmBank = new Shm437(1,sizeof(BANK)); // set up pointers
BANK *ptrBank = (BANK*) pShmBank->ShmAlloc();
ptrBank->balance[0] = ptrBank->balance[1] = 100;
Semaphore(1); // initialize Semaphore class, pass sig
srandom(getpid()); // set seed
printf("Init balances 0:%d + 1:%d ==> %d!\n", // print initial
ptrBank->balance[0],
ptrBank->balance[1],
ptrBank->balance[0]+ptrBank->balance[1]
);
if(pthread_create(&tid1, NULL, crit_area, ptrBank)) // p thread 1
{
printf("\n ERROR creating thread 1");
exit(1);
}
if(pthread_create(&tid2, NULL, crit_area, ptrBank)) // p thread 2
{
printf("\n ERROR creating thread 2");
exit(1);
}
if(pthread_join(tid1, NULL)) //wait for the thread 1 to finish
{
printf("\n ERROR joining thread");
exit(1);
}
if(pthread_join(tid2, NULL)) // wait for the thread 2 to finish
{
printf("\n ERROR joining thread");
exit(1);
}
// print new values
printf("Let's check the balances 0:%d + 1:%d ==> %d ?= 200\n",
ptrBank->balance[0],ptrBank->balance[1],
ptrBank->balance[0]+ptrBank->balance[1]);
pthread_exit(NULL);
Semaphore(1);
}
/* Thread Function critical area where we will use semaphore
and create balance with timing delay */
void * crit_area(BANK * a)
{
int tmp1, tmp2, rint;
double dummy;
wait(0);
for (int i=0; i < 100; i++) // loop for 99 times
{
tmp1 = a->balance[0]; // tmp1 var is equal to balance[0] or 100 //line 49
tmp2 = a->balance[1]; // tmp2 var is equal to balance[1] or 100
rint = (rand()%20)-10; // rint var == random number (0 thru 19)-10 or -10 thru 9
// using the seed number (PID) as the starting value
if ((tmp1+rint)>=0 && (tmp2-rint)>=0) // if non-negative
{
a->balance[0] = tmp1 + rint; // if not, change balance[0] to tmp1+rint //line 55
for (int j=0; j < rint*100; j++) // run this math problem for rint*100 times
{dummy=2.345*8.765/1.234; }
usleep(1);
a->balance[1] = tmp2 - rint; // change balance[1] to tmp2-rint //line 63
}
}
signal(1);
}
Pthreads expects functions with a signature that takes void*, not BANK*. You should change the crit_area function as follows:
void * crit_area(void* voidA) {
BANK *a = (BANK*)voidA;
... // The rest of your function
}
I'm trying to write a function that can Shift out data to 74HC595 shift registers which can shift out 8, 16, and 32 bit values.
Using an overloaded function, I have this:
/**********************************************************************************
* Software SPI Pin Settings
*********************************************************************************/
#define SPIPINPORT PORTB //The Port that the Pins are on.
#define LatchPin 2 //_RCLK Shift register clock pin
#define DataPin 3 //SER DS Serial data input
#define ClockPin 5
/**********************************************************************************
* Preproccesor PIN to PIN Mask
*********************************************************************************/
#define LATCHMASK (1 << LatchPin)
#define MOSIMASK (1 << DataPin)
#define CLOCKMASK (1 << ClockPin)
/**********************************************************************************
* Macros
*********************************************************************************/
#define tggl(port,bit) (port)^=(1<<(bit))
#define LATCH (SPIPINPORT &=~ LATCHMASK)
#define unLATCH (SPIPINPORT |= LATCHMASK)
#define PULSE { tggl(SPIPINPORT,ClockPin); tggl(SPIPINPORT,ClockPin); }
void zShiftClass::ShiftOut(uint8_t value)
{
LATCH;
for (uint8_t i = 0; i <= 7; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
void zShiftClass::ShiftOut(uint16_t value)
{
LATCH;
for (uint8_t i = 0; i <= 15; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
void zShiftClass::ShiftOut(uint32_t value)
{
LATCH;
for (uint8_t i = 0; i <= 31; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
And I want to use this template to replace these functions:
template<typename TYPE>void Shift(TYPE value)
{
uint8_t loops = (( 8 * sizeof(value) ) - 1 );
LATCH;
for (uint8_t i = 0; i <= loops; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
When I compile, I get the following errors:
Compiling 'zLEDArray' for 'Arduino Uno'
zLEDArray.ino : variable or field 'Shift' declared void
zLEDArray.ino : 'TYPE' was not declared in this scope
Error compiling
What am I doing wrong?
Okay, this falls in the category of "beware of dev tools bearing gifts". The Arduino sketch tool is your problem. If you turn on verbose compiler output on the preferences menu, you will get some insight into what happens. With your code, I can duplicate your error. When compiling a test project called template1, the same error is reported, but now can see the compiler command line:
D:\arduino-dev\arduino-1.0.3\hardware\tools\avr\bin\avr-g++ -c ...yada yada
more yada... e:\Temp\build3528223623599856131.tmp\template1.cpp ...
template1:14: error: variable or field 'Shift' declared void
template1:14: error: 'TYPE' was not declared in this scope
The key point is that .CPP file. That is a file that the dev environment constructs from your .INO and is what is the actual input to the compiler. If you go grab that file, you will see all your code with some bonus lines included:
#include "Arduino.h"
void Shift(TYPE value);
void setup();
void loop();
The build tool added for you, 4 lines:
the Arduino header (because nobody remembers this)
3 forward declarations for functions that it figured out by parsing code
The attempt at producing a forward declaration from the function template is incorrect, and produces the code that results in the compiler error.
The solution is to move the template out from the .INO file.
Create a library folder, say T1.
Create a .H file in that folder with the template code, say tspi.h.
Import the library to your project.
Make sure the #include line is after the first line of code in your .INO (more weirdness - the tool will insert a #include "Arduino.h" after all the comments but before first line of code. If you leave your include at the top of the .INO file, it will be processed before the Arduino header)
You can get rid of the error by adding the correct forward declaration in your ino file. The inuntuitive thing is that you must also do this if you define the function before you use it:
template <typename TYPE> void Shift(TYPE value);
// you may use the function here or you can have the declaration
// immediately before the definition
template<typename TYPE>void Shift(TYPE value)
{
// your implementation
}
The explanation why this is the case is in jdr5ca's answer. Thanks for clarifying, I found this out by trial and error.