ESP32TimerInterrupt - unsolved variables (missing inclusion?) - timer

I'm testing the library ESP32TimerInterrupt for ESP32 using the example ISR_16_Timars_Array. While compiling I get errors of missing declarations about the method timer_isr_callback_add and the parameters TIMER_INTR_T0 and TIMER_INTR_T1. Although all the h files included with the library are available to the example, I assume I miss some file inclusion - which one?
And another question - I'm using the board Heltec WiFILoRa 32(V2). To minimize interference with other HW/SW features I prefer using timer 2 - is it possible?
Best regards
Yons Gy
The error messages:
Arduino: 1.8.19 (Windows 10), Board: "WiFi LoRa 32(V2), Disabled, 240MHz (WiFi/BT), 921600, None, REGION_EU868, None"
In file included from D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ISR_16_Timers_Array.ino:23:0:
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'bool ESP32TimerInterrupt::setFrequency(const float&, bool (* const&)(void*))':
ESP32TimerInterrupt.hpp:329:100: error: 'timer_isr_callback_add' was not declared in this scope
timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::detachInterrupt()':
ESP32TimerInterrupt.hpp:371:66: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:371:82: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::disableTimer()':
ESP32TimerInterrupt.hpp:380:66: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:380:82: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::reattachInterrupt()':
ESP32TimerInterrupt.hpp:390:65: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:390:81: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
D:\Current\Tractor\TractorSW\motion\src\ISR_16_Timers_Array\ESP32TimerInterrupt.hpp: In member function 'void ESP32TimerInterrupt::enableTimer()':
ESP32TimerInterrupt.hpp:400:65: error: 'TIMER_INTR_T0' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
ESP32TimerInterrupt.hpp:400:81: error: 'TIMER_INTR_T1' was not declared in this scope
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
^
exit status 1
'timer_isr_callback_add' was not declared in this scope
The code (first - ESP32TimerInterrupt.hpp - library file), then the .ino file:
/****************************************************************************************************************************
ESP32TimerInterrupt.hpp
For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
Written by Khoi Hoang
Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
Licensed under MIT license
The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
The timer counters can be configured to count up or down and support automatic reload and software reload.
They can also generate alarms when they reach a specific value, defined by the software.
The value of the counter can be read by the software program.
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
Therefore, their executions are not blocked by bad-behaving functions / tasks.
This important feature is absolutely necessary for mission-critical tasks.
Based on SimpleTimer - A timer library for Arduino.
Author: mromani#ottotecnica.com
Copyright (c) 2010 OTTOTECNICA Italy
Based on BlynkTimer.h
Author: Volodymyr Shymanskyy
Version: 2.0.1
Version Modified By Date Comments
------- ----------- ---------- -----------
1.0.0 K Hoang 23/11/2019 Initial coding
1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library
1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked
1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README.
1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error.
1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval
1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage
1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2
1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries.
1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1
1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error
2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library.
2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code
*****************************************************************************************************************************/
#pragma once
#ifndef ESP32TIMERINTERRUPT_HPP
#define ESP32TIMERINTERRUPT_HPP
#if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \
ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \
ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM )
#define USING_ESP32_S2_TIMERINTERRUPT true
#elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \
defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) )
#define USING_ESP32_S3_TIMERINTERRUPT true
#elif ( ARDUINO_ESP32C3_DEV )
#define USING_ESP32_C3_TIMERINTERRUPT true
#elif defined(ESP32)
#define USING_ESP32_TIMERINTERRUPT true
#else
#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
#endif
#ifndef ESP32_TIMER_INTERRUPT_VERSION
#define ESP32_TIMER_INTERRUPT_VERSION "ESP32TimerInterrupt v2.0.1"
#define ESP32_TIMER_INTERRUPT_VERSION_MAJOR 2
#define ESP32_TIMER_INTERRUPT_VERSION_MINOR 0
#define ESP32_TIMER_INTERRUPT_VERSION_PATCH 1
#define ESP32_TIMER_INTERRUPT_VERSION_INT 2000001
#endif
#ifndef TIMER_INTERRUPT_DEBUG
#define TIMER_INTERRUPT_DEBUG 0
#endif
#if defined(ARDUINO)
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#endif
#include "TimerInterrupt_Generic_Debug.h"
#include <driver/timer.h>
#include "SimpleTimer.h" // https://github.com/jfturcot/SimpleTimer
/*
//ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h:
#define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups
//#brief Selects a Timer-Group out of 2 available groups
typedef enum
{
TIMER_GROUP_0 = 0, //!<Hw timer group 0
TIMER_GROUP_1 = 1, //!<Hw timer group 1
TIMER_GROUP_MAX,
} timer_group_t;
//#brief Select a hardware timer from timer groups
typedef enum
{
TIMER_0 = 0, //!<Select timer0 of GROUPx
TIMER_1 = 1, //!<Select timer1 of GROUPx
TIMER_MAX,
} timer_idx_t;
//#brief Decides the direction of counter
typedef enum
{
TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low
TIMER_COUNT_UP = 1, //Ascending Count from Zero
TIMER_COUNT_MAX
} timer_count_dir_t;
//#brief Decides whether timer is on or paused
typedef enum
{
TIMER_PAUSE = 0, //Pause timer counter
TIMER_START = 1, //Start timer counter
} timer_start_t;
//#brief Decides whether to enable alarm mode
typedef enum
{
TIMER_ALARM_DIS = 0, //Disable timer alarm
TIMER_ALARM_EN = 1, //Enable timer alarm
TIMER_ALARM_MAX
} timer_alarm_t;
//#brief Select interrupt type if running in alarm mode.
typedef enum
{
TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode
//TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now
TIMER_INTR_MAX
} timer_intr_mode_t;
//#brief Select if Alarm needs to be loaded by software or automatically reload by hardware.
typedef enum
{
TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event
TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event
TIMER_AUTORELOAD_MAX,
} timer_autoreload_t;
//#brief Data structure with timer's configuration settings
typedef struct
{
bool alarm_en; //Timer alarm enable
bool counter_en; //Counter enable
timer_intr_mode_t intr_type; //Interrupt mode
timer_count_dir_t counter_dir; //Counter direction
bool auto_reload; //Timer auto-reload
uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536.
} timer_config_t;
*/
class ESP32TimerInterrupt;
typedef ESP32TimerInterrupt ESP32Timer;
#if USING_ESP32_C3_TIMERINTERRUPT
#define MAX_ESP32_NUM_TIMERS 2
#else
#define MAX_ESP32_NUM_TIMERS 4
#endif
#define TIMER_DIVIDER 80 // Hardware timer clock divider
// TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds
// In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h
// typedef bool (*timer_isr_t)(void *);
//esp_err_t timer_isr_callback_add(timer_group_t group_num, timer_idx_t timer_num, timer_isr_t isr_handler, void *arg, int intr_alloc_flags);
//esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num);
//timer_deinit(timer_group_t group_num, timer_idx_t timer_num);
//esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask);
//esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask);
typedef bool (*esp32_timer_callback) (void *);
// For ESP32_C3, TIMER_MAX == 1
// For ESP32 and ESP32_S2, TIMER_MAX == 2
typedef struct
{
timer_idx_t timer_idx;
timer_group_t timer_group;
//int alarm_interval;
//timer_autoreload_t auto_reload;
} timer_info_t;
class ESP32TimerInterrupt
{
private:
timer_config_t stdConfig =
{
.alarm_en = TIMER_ALARM_EN, //enable timer alarm
.counter_en = TIMER_START, //starts counting counter once timer_init called
.intr_type = TIMER_INTR_MAX,
.counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value
.auto_reload = TIMER_AUTORELOAD_EN, // reloads counter automatically
.divider = TIMER_DIVIDER
};
timer_idx_t _timerIndex;
timer_group_t _timerGroup;
uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1
uint8_t _timerNo;
esp32_timer_callback _callback; // pointer to the callback function
float _frequency; // Timer frequency
uint64_t _timerCount; // count to activate timer
//xQueueHandle s_timer_queue;
public:
ESP32TimerInterrupt(const uint8_t& timerNo)
{
_callback = NULL;
if (timerNo < MAX_ESP32_NUM_TIMERS)
{
_timerNo = timerNo;
#if USING_ESP32_C3_TIMERINTERRUPT
// Always using TIMER_INTR_T0
_timerIndex = (timer_idx_t) ( (uint32_t) 0 );
// timerNo == 0 => Group 0, timerNo == 1 => Group 1
_timerGroup = (timer_group_t) ( (uint32_t) timerNo);
#else
_timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX);
_timerGroup = (timer_group_t) (_timerNo / TIMER_MAX);
#endif
}
else
{
_timerNo = MAX_ESP32_NUM_TIMERS;
}
};
// frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
bool setFrequency(const float& frequency, const esp32_timer_callback& callback)
{
if (_timerNo < MAX_ESP32_NUM_TIMERS)
{
// select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now.
// Will use later if very low frequency is needed.
_frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000;
_timerCount = (uint64_t) _frequency / frequency;
// count up
#if USING_ESP32_S2_TIMERINTERRUPT
TISR_LOGWARN3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
#elif USING_ESP32_S3_TIMERINTERRUPT
// ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers
// and 54-bit auto-reload-capable up/down-timers
TISR_LOGWARN3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
#else
TISR_LOGWARN3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
#endif
timer_init(_timerGroup, _timerIndex, &stdConfig);
// Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP
timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency);
// enable interrupts for _timerGroup, _timerIndex
timer_enable_intr(_timerGroup, _timerIndex);
_callback = callback;
// Register the ISR handler
// If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute
// and can only call functions in IRAM or ROM. It cannot call other timer APIs.
//timer_isr_register(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, ESP_INTR_FLAG_IRAM, NULL);
timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0);
timer_start(_timerGroup, _timerIndex);
return true;
}
else
{
#if USING_ESP32_C3_TIMERINTERRUPT
TISR_LOGERROR(F("Error. Timer must be 0-1"));
#else
TISR_LOGERROR(F("Error. Timer must be 0-3"));
#endif
return false;
}
}
// interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
bool setInterval(const unsigned long& interval, const esp32_timer_callback& callback)
{
return setFrequency((float) (1000000.0f / interval), callback);
}
bool attachInterrupt(const float& frequency, const esp32_timer_callback& callback)
{
return setFrequency(frequency, callback);
}
// interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
bool attachInterruptInterval(const unsigned long& interval, const esp32_timer_callback& callback)
{
return setFrequency( (float) ( 1000000.0f / interval), callback);
}
void detachInterrupt()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
void disableTimer()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
// Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
void reattachInterrupt()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
// Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
void enableTimer()
{
#if USING_ESP32_C3_TIMERINTERRUPT
timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
#else
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
#endif
}
// Just stop clock source, clear the count
void stopTimer()
{
timer_pause(_timerGroup, _timerIndex);
}
// Just reconnect clock source, start current count from 0
void restartTimer()
{
timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
timer_start(_timerGroup, _timerIndex);
}
int8_t getTimer() __attribute__((always_inline))
{
return _timerIndex;
};
int8_t getTimerGroup() __attribute__((always_inline))
{
return _timerGroup;
};
}; // class ESP32TimerInterrupt
#include "ESP32_ISR_Timer.hpp"
#endif // ESP32TIMERINTERRUPT_HPP
INO file:
// These define's must be placed at the beginning before #include "ESP32TimerInterrupt.h"
#define _TIMERINTERRUPT_LOGLEVEL_ 4
#define HW_TIMER_INTERVAL_MS 1L
#define NUMBER_ISR_TIMERS 2
#define LED_TOGGLE_INTERVAL_MS 2000L
#define BLOCKING_TIME_MS 10000L
#define LED 25
long blockingTime;
void doingSomething0(){}
void doingSomething1(){}
uint32_t TimerInterval[NUMBER_ISR_TIMERS] = {1000L, 1500L};
typedef void (*irqCallback)();
irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
{
doingSomething0,
doingSomething1
};
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "ESP32TimerInterrupt.hpp"
//#include "ESP32_ISR_Timer.h"
//#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer
volatile uint32_t startMillis = 0;
ESP32Timer ITimer(1); // Init ESP32 timer 1
ESP32_ISR_Timer ISR_Timer; // Init ESP32_ISR_Timer
/************************************/
bool IRAM_ATTR TimerHandler(void * timerNo)
{
static int timeRun = 0;
ISR_Timer.run();
// Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS))
{
timeRun = 0;
//timer interrupt toggles pin LED_BUILTIN
digitalWrite(LED, !digitalRead(LED));
}
return true;
}
/************************************/
void setup()
{
Serial.begin(115200);
while (!Serial);
pinMode(LED, OUTPUT);
delay(200);
Serial.print(F("\nStarting ISR_16_Timers_Array on "));
Serial.println(ARDUINO_BOARD);
Serial.print(ESP32_TIMER_INTERRUPT_VERSION);
Serial.print(F(" runninr at CPU Frequency = "));
Serial.print(F_CPU / 1000000);
Serial.println(F(" MHz"));
// Interval in microsecs
if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler))
{
startMillis = millis();
Serial.print(F("Starting ITimer OK, millis() = "));
Serial.println(startMillis);
}
else
Serial.println(F("Can't set ITimer. Select another freq. or timer"));
// You can use up to 16 timer for each ISR_Timer
for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
}
/************************************/
void loop()
{
if ((millis() - blockingTime) < BLOCKING_TIME_MS)
return;
blockingTime = millis();
}

Related

Is this the right way to access function?

I am currently using "STM32F429I-DISC1" with joystick. I am trying to draw something on the LCD screen and using joystick move this object. My drawing is working fine, but I have the error: " void value not ignored as it ought to be".
This two lines have problems...
localX = Joy_ReadXY(CTRL_REG_IN3);
localY = Joy_ReadXY(CTRL_REG_IN4);
Can someone please tell me, how I can fix this error?
And why I see this error?
Main.c
#include "stm32f429i_discovery_lcd.h"
#define CTRL_REG_IN3 0b00011000
#define CTRL_REG_IN4 0b00100000
SemaphoreHandle_t xMutex;
Joystick_data xy;
void vTaskFunction1(void *pvParameters) {
uint16_t localX;
uint16_t localY;
for(;;) {
localX = Joy_ReadXY(CTRL_REG_IN3);
localY = Joy_ReadXY(CTRL_REG_IN4);
xSemaphoreTake( xMutex, portMAX_DELAY );
xy.x = localX;
xy.y = localY;
xSemaphoreGive( xMutex );
HAL_Delay(10);
}
}
void vTaskFunction2(void *pvParameters) {
uint32_t xCoord = 240/2;
uint32_t yCoord = 320/2;
uint8_t reads = 0;
uint8_t ballRadius = 5;
uint16_t xLimitMin = ballRadius+25;
uint16_t xLimitMax = 240-ballRadius-25;
uint16_t yLimitMin = ballRadius+25;
uint16_t yLimitMax = 320-ballRadius-25;
for(;;) {
xSemaphoreTake( xMutex, portMAX_DELAY );
if (xy.x > 3000 && !(xCoord < xLimitMin))
xCoord -= 5;
if (xy.x < 1000 && !(xCoord > xLimitMax))
xCoord += 5;
if (xy.y > 3000 && !(yCoord < yLimitMin))
yCoord -= 5;
if (xy.y < 1000 && !(yCoord > yLimitMax))
yCoord += 5;
reads++;
BSP_LCD_Clear(LCD_COLOR_WHITE);
BSP_LCD_DrawCircle(xCoord, yCoord, ballRadius);
BSP_LCD_FillCircle(xCoord, yCoord, ballRadius);
xSemaphoreGive(xMutex);
HAL_Delay(20);
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI4_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
// LCD Things
BSP_LCD_Init();
BSP_LCD_LayerDefaultInit(1, LCD_FRAME_BUFFER);
BSP_LCD_SelectLayer(1);
BSP_LCD_SetBackColor(LCD_COLOR_WHITE); // Vali meelepärane värv
BSP_LCD_Clear(LCD_COLOR_WHITE);
BSP_LCD_SetTextColor(LCD_COLOR_DARKBLUE); // Vali meelepärane värv
MX_FREERTOS_Init();
if ( xMutex == NULL )
{
xMutex = xSemaphoreCreateMutex();
if ( ( xMutex ) != NULL )
xSemaphoreGive( ( xMutex ) );
}
xTaskCreate(vTaskFunction1, "Task 1", 100, NULL, 1, NULL);
xTaskCreate(vTaskFunction2, "Task 2", 100, NULL, 1, NULL);
vTaskStartScheduler();
osKernelStart();
while (1)
{
}
}
Read joystick function (joystick.c)
#include <stdio.h>
#include <main.h>
#include "gpio.h"
#include "spi.h"
#define READ_SLAVE_OPERATION 0b10000000
#define READ_INCR_SLAVE_OPERATION 0b11000000
#define WRITE_SLAVE_OPERATION 0b00000000
#define CTRL_REG_IN3 0b00000011
#define CTRL_REG_IN4 0b00000100
#define OUT_X_L 0x28
#define OUT_X_H 0x29
#define OUT_Y_L 0x2A
#define OUT_Y_H 0x2B
#define OUT_Z_L 0x2C
#define OUT_Z_H 0x2D
#define JOY_CS_LOW() HAL_GPIO_WritePin(JOY_CS_GPIO_PORT, JOY_CS_PIN, 0)
#define JOY_CS_HIGH() HAL_GPIO_WritePin(JOY_CS_GPIO_PORT, JOY_CS_PIN, 1)
#define JOY_CS_GPIO_PORT GPIOC
#define JOY_CS_PIN GPIO_PIN_13
int16_t Joy_ReadXY(uint8_t reg1){
uint8_t pTxData1[2] = {reg1, 0};
uint8_t pRxData1[2] = {0, 0};
JOY_CS_LOW();
HAL_SPI_TransmitReceive(&hspi4, pTxData1, pRxData1, 2, HAL_MAX_DELAY);
JOY_CS_HIGH();
return pRxData1[0] << 8 | pRxData1[1];
}
Here, in Main.c, you call the function before telling the compiler about what parameters and what return value types it has.
localX = Joy_ReadXY(CTRL_REG_IN3);
localY = Joy_ReadXY(CTRL_REG_IN4)
That confused the compiler and it starts "guessing" about them.
Guessing that it is a void-returning function, the compiler then complains that you are expecting a return value from a function which does return void i.e. nothing.
The returned void should be ignored, instead of attempting to write it to a variable. At least that is what the compiler thinks...
To fix it, you should explain to the compiler that there is a function elsewhere, with name, parameters and return value type. That is done by providing the prototype
int16_t Joy_ReadXY(uint8_t reg1);
It needs to be done before the function body in which the the extern function is first called. (And you already confirmed in comments that it fixes the described problem in your code.)
Note that for the other shown functions this is not needed, because they are defined (with head and body) before they are called.
Similar for other functions, which have their prototype provided in the header you include early on.
Actually, putting the prototype of your function into a header and including that similarily would be the best way to solve this.

errors encountered while interfacing eeprom with microcontroller

I am not an expert c programmers and in the c code I m getting these kinds of errors. I got many and tried to sort them out but can not solve these. The code is as follows:
/*
* EEPROM.c
* interfacing microchip 24aa64f IC with atmel sam4e
*/
#include <asf.h>
#include "EEPROM_I2C.h"
#define DEVICE_ADDRESS 0x50 // 7-bit device identifier 0101000, (refer datasheet)
//#define EEPROM_NAME 24AA6F
#define I2C_FAST_MODE_SPEED 400000//TWI_BUS_CLOCK 400KHz
#define TWI_CLK_DIVIDER 2
#define TWI_CLK_DIV_MIN 7
#define TWI_CLK_CALC_ARGU 4
#define TWI_CLK_DIV_MAX 0xFF
/*************************** Main function ******************************/
int eeprom_main( void )
{
struct micro24 ptMicro24 ;
typedef struct twi_options twi_options_t;
typedef struct Twi_registers Twi;
char TxBuffer[128] ;
char RxBuffer[128] ;
int BufferIndex;
unsigned int PageCount;
unsigned int error = 0 ;
unsigned int i;
ptMicro24.PageSize = 32;
ptMicro24.NumOfPage = 128;
ptMicro24.EepromSize = 128*32;
ptMicro24.SlaveAddress = DEVICE_ADDRESS;
ptMicro24.EepromName = 64;
/***************************** CLOCK SETTINGS TO GET 400KHz **********************
* Set the I2C bus speed in conjunction with the clock frequency.
* param p_twi Pointer to a TWI instance.
* return value PASS\Fail New speed setting is accepted\rejected
**********************************************************************************/
uint32_t twi_set_speed(struct Twi_registers *Twi, uint32_t ul_speed, uint32_t ul_mck)
//uint32_t twi_set_speed(Twi *p_twi, uint32_t ul_speed, uint32_t ul_mck)
{
uint32_t ckdiv = 0; //clock divider is used to increase both TWCK high and low periods (16-18)
uint32_t c_lh_div; //CHDIV (0-7) and CLDIV (8-15)
if (ul_speed > I2C_FAST_MODE_SPEED) { //ul_speed is the desired I2C bus speed
return FAIL;
}
c_lh_div = ul_mck / (ul_speed * TWI_CLK_DIVIDER) - TWI_CLK_CALC_ARGU; //ul_mck main clock of the device
/* cldiv must fit in 8 bits, ckdiv must fit in 3 bits */
while ((c_lh_div > TWI_CLK_DIV_MAX) && (ckdiv < TWI_CLK_DIV_MIN))
{
ckdiv++; // Increase clock divider
c_lh_div /= TWI_CLK_DIVIDER; //Divide cldiv value
}
/* set clock waveform generator register */
Twi->TWI_CWGR =
TWI_CWGR_CLDIV(c_lh_div) | TWI_CWGR_CHDIV(c_lh_div) |
TWI_CWGR_CKDIV(ckdiv);
return PASS;
}
/************************************ Initialize TWI master mode ************************
* Set the control register TWI_CR by MSEN and SVDIS
* param p_opt Options for initializing the TWI module
* return TWI_SUCCESS if initialization is complete
* twi_options... structure contains clock speed, master clock, chip and smbus
*****************************************************************************************/
uint32_t twi_master_start(struct Twi_registers *Twi, struct twi_options_t *twi_options_t)
//uint32_t twi_master_start(Twi *p_twi, const twi_options_t *p_opt)
{
uint32_t status = TWI_SUCCESS; // status success return code is 0
// Enable master mode and disable slave mode in TWI_CR
Twi -> TWI_CR_START = TWI_CR_START;
Twi->TWI_CR_MSEN = TWI_CR_MSEN; // Set Master Enable bit
Twi->TWI_CR_SVDIS = TWI_CR_SVDIS; // Set Slave Disable bit
/* Select the speed */
//new//if (twi_set_speed(Twi->TWI_SR, twi_options_t->speed, twi_options_t->master_clk) == FAIL)
//if (twi_set_speed(Twi, twi_options_t->speed, twi_options_t->master_clk) == FAIL)
//{
//status = TWI_INVALID_ARGUMENT; /* The desired speed setting is rejected */
//}
if (twi_options_t->smbus == 0)
{
Twi->TWI_CR_QUICK == 0;
status = TWI_INVALID_ARGUMENT;
}
else
if (twi_options_t->smbus == 1)
{
Twi->TWI_CR_QUICK == 1;
status = TWI_SUCCESS;
}
return status;
}
/***************************** WriteByte Function ********************************
This function uses a two bytes internal address (IADR) along with
Internal word address of eeprom.
Return Value: None
***********************************************************************************/
void WriteByte (struct micro24 *ptMicro24, char Data2Write,
unsigned int Address)
//Data2Write is the data to be written n the eeprom
//struct <micro24 *ptMicro24> : Structure of Microchip 24AA Two-wire Eeprom
//unsigned int Address>: Address where to write
{
unsigned int WordAddress;
unsigned int SlaveAddress;
unsigned char p0=0;
TWI_CR_START ==1;
if (ptMicro24->EepromName == 64 )
{
if ( Address > 0xFFFF)
{
p0 = 1;
/* Mask the 17th bit to get the 16th LSB */
WordAddress = Address & 0xFFFF ;
SlaveAddress = ptMicro24->SlaveAddress + (p0<<16) ;
}
else {
SlaveAddress = ptMicro24->SlaveAddress ;
WordAddress = Address ;
}
}
TWI_CR_STOP ==1;
//TWI_WriteSingleIadr(TWI_IADR_IADR,SlaveAddress, WordAddress,
// TWI_MMR_IADRSZ_2_BYTE, &Data2Write); // declared as extern
// to write to internal address, utilizing internal address and master mode register
//}
/******************** Increase Speed Function *****************************
* TWI is accessed without calling TWI functions
/***************************************************************************/
int NumOfBytes, Count;
int status;
uint32_t Buffer;
/* Enable Master Mode of the TWI */
TWI_CR_MSEN == 1;
// Twi.TWI_CR_MSEN ==1;
//TWI_CR->TWI_CR_MSEN = TWI_CR_MSEN ;
/* Set the TWI Master Mode Register */
Twi->TWI_MMR = (SlaveAddress & (~TWI_MMR_MREAD) | (TWI_MMR_IADRSZ_2_BYTE));
/* Set the internal address to access the wanted page */
Twi -> TWI_IADR = WordAddress ;
/* Wait until TXRDY is high to transmit the next data */
status = TWI_SR_TXRDY;
while (!(status & TWI_SR_TXRDY))
status = TWI_SR_TXRDY;
/* Send the buffer to the page */
for (Count=0; Count < NumOfBytes ;Count++ )
{
Twi ->TWI_THR_TXDATA = Buffer++;
/* Wait until TXRDY is high to transmit the next data */
status = TWI_SR_TXRDY;
while (!(status & TWI_SR_TXRDY))
status = TWI_SR_TXRDY;
}
/* Wait for the Transmit complete is set */
status = TWI_SR_TXCOMP;
while (!(status & TWI_SR_TXCOMP))
status = TWI_SR_TXCOMP;
// add some wait function according to datasheet before sending the next data
// e.g: 10ms
// e.g: WaitMiliSecond (10);
}
/****************************** ReadByte Function **************************
This function uses a two bytes internal address (IADR) along with
Internal word address of eeprom.
Return Value: None
****************************************************************************/
char ReadByte (struct micro24 *ptMicro24,
unsigned int Address) //int Address to read
{
unsigned int WordAddress;
unsigned int SlaveAddress;
char Data2Read ;
unsigned char p0=0;
TWI_CR_START == 1;
//p_twi -> TWI_CR_START = TWI_CR_START;
if (ptMicro24->EepromName == 64)
{
if ( Address > 0xFFFF) {
p0 = 1;
// Mask the 17th bit to get the 16th LSB
WordAddress = Address & 0xFFFF ;
SlaveAddress = ptMicro24->SlaveAddress + (p0<<16) ;
}
else {
SlaveAddress = ptMicro24->SlaveAddress ;
WordAddress = Address ;
}
}
//TWI_ReadSingleIadr(TWI_IADR_IADR,SlaveAddress,WordAddress,
// TWI_MMR_IADRSZ_2_BYTE,&Data2Read);
// declared as extern
// to write to internal address, utilizing internal address and master mode register
return (Data2Read);
}
}
errors are:
(24,19): error: storage size of 'ptMicro24' isn't known
67,5): error: dereferencing pointer to incomplete type
Twi->TWI_CWGR =
error: expected identifier before '(' token
#define TWI_CR_START (0x1u << 0) /**< \brief (TWI_CR) Send a START Condition */
error: expected identifier before '(' token
#define TWI_CR_MSEN (0x1u << 2) /**< \brief (TWI_CR) TWI Master Mode Enabled */
error: expected identifier before '(' token
#define TWI_CR_SVDIS (0x1u << 5) /**< \brief (TWI_CR) TWI Slave Mode Disabled */
error: dereferencing pointer to incomplete type
if (twi_options_t->smbus == 0)
It seems missing the declaration of struct micro24, this may be the cause of first error: error: storage size of 'ptMicro24' isn't known.
The same for declaration of Twi_registers, that is causing other errors.
Either you forgot to declare these structs or to include an header file declaring them.

How to make a microsecond-precise timer on the STM32L-Discovery ARM board?

I'm trying to implement the Dallas OneWire protocol, but I'm having trouble generating a microsecond delay on the STM32l-Discovery.
How do I implement a timer accurate enough to delay the program for x microseconds?
For start I must tell you that there is no way to accomplish a precise usec delay using software. Even if you use an interrupt based system you will have latencies. Off course you can achieve a better accuracy with a larger CPU frequencies.
In order to connect with a 1-Wire device you can use:
A external interface like DS2482-100
A software 1-wire implementation using pin polling.
For the second solution you have to call a software based delay. You can make a flag polling delay or an interrupt based flag polling delay. In both cases you will be sure that a certain amount of time has passed but you can not be sure how match more time has passed. This is because of the CPU latency, the CPU clock etc...
For example consider the following implementation. We program a HW TIMER to continuously count up and we check TIMER's value. We name "jiffy" the time between each TIMER's ticks and jiffies the TIMERS max value:
Low level driver part (ex: driver.h)
// ...
#define JF_TIM_VALUE (TIM7->CNT)
int JF_setfreq (uint32_t jf_freq, uint32_t jiffies);
// ...
Low level driver part (ex: driver.c)
// ...
#include <stm32l1xx.h>
#include <misc.h>
#include <stm32l1xx_rcc.h>
#include <stm32l1xx_tim.h>
/*
* Time base configuration using the TIM7
* \param jf_freq The TIMER's frequency
* \param jiffies The TIMER's max count value
*/
int JF_setfreq (uint32_t jf_freq, uint32_t jiffies) {
uint32_t psc=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
SystemCoreClockUpdate ();
if (jf_freq)
psc = (SystemCoreClock / jf_freq) - 1;
if (psc < 0xFFFF) TIM7->PSC = psc;
else return 1;
if (jiffies < 0xFFFF) TIM7->ARR = jiffies;
else return 1;
TIM7->CR1 |= TIM_CR1_CEN;
return 0;
}
// ...
Middleware jiffy system with some delay implementations
jiffy.h:
#include "string.h"
typedef int32_t jiffy_t; // Jiffy type 4 byte integer
typedef int (*jf_setfreq_pt) (uint32_t, uint32_t); //Pointer to setfreq function
typedef volatile struct {
jf_setfreq_pt setfreq; // Pointer to driver's timer set freq function
jiffy_t *value; // Pointer to timers current value
uint32_t freq; // timer's frequency
uint32_t jiffies; // jiffies max value (timer's max value)
jiffy_t jpus; // Variable for the delay function
}jf_t;
/*
* ============= PUBLIC jiffy API =============
*/
/*
* Link functions
*/
void jf_link_setfreq (jf_setfreq_pt pfun);
void jf_link_value (jiffy_t* v);
/*
* User Functions
*/
void jf_deinit (void);
int jf_init (uint32_t jf_freq, uint32_t jiffies);
jiffy_t jf_per_usec (void);
void jf_delay_us (int32_t usec);
int jf_check_usec (int32_t usec);
jiffy.c:
#include "jiffy.h"
static jf_t _jf;
#define JF_MAX_TIM_VALUE (0xFFFF) // 16bit counters
//Connect the Driver's Set frequency function
void jf_link_setfreq (jf_setfreq_pt pfun) {
_jf.setfreq = pfun;
}
// Connect the timer's value to jiffy struct
void jf_link_value (jiffy_t* v) {
_jf.value = v;
}
// De-Initialize the jf data and un-connect the functions
// from the driver
void jf_deinit (void) {
memset ((void*)&_jf, 0, sizeof (jf_t));
}
// Initialise the jf to a desired jiffy frequency f
int jf_init (uint32_t jf_freq, uint32_t jiffies) {
if (_jf.setfreq) {
if ( _jf.setfreq (jf_freq, jiffies) )
return 1;
_jf.jiffies = jiffies;
_jf.freq = jf_freq;
_jf.jpus = jf_per_usec ();
return 0;
}
return 1;
}
// Return the systems best approximation for jiffies per usec
jiffy_t jf_per_usec (void) {
jiffy_t jf = _jf.freq / 1000000;
if (jf <= _jf.jiffies)
return jf;
else
// We can not count beyond timer's reload
return 0;
}
/*!
* \brief
* A code based delay implementation, using jiffies for timing.
* This is NOT accurate but it ensures that the time passed is always
* more than the requested value.
* The delay values are multiplications of 1 usec.
* \param
* usec Time in usec for delay
*/
void jf_delay_us (int32_t usec) {
jiffy_t m, m2, m1 = *_jf.value;
usec *= _jf.jpus;
if (*_jf.value - m1 > usec) // Very small delays will return here.
return;
// Delay loop: Eat the time difference from usec value.
while (usec>0) {
m2 = *_jf.value;
m = m2 - m1;
usec -= (m>0) ? m : _jf.jiffies + m;
m1 = m2;
}
}
/*!
* \brief
* A code based polling version delay implementation, using jiffies for timing.
* This is NOT accurate but it ensures that the time passed is always
* more than the requested value.
* The delay values are multiplications of 1 usec.
* \param
* usec Time in usec for delay
*/
int jf_check_usec (int32_t usec) {
static jiffy_t m1=-1, cnt;
jiffy_t m, m2;
if (m1 == -1) {
m1 = *_jf.value;
cnt = _jf.jpus * usec;
}
if (cnt>0) {
m2 = *_jf.value;
m = m2-m1;
cnt-= (m>0) ? m : _jf.jiffies + m;
m1 = m2;
return 1; // wait
}
else {
m1 = -1;
return 0; // do not wait any more
}
}
Hmmm you made it till here. Nice
So now you can use it in your application like this:
main.c:
#include "driver.h"
#include "jiffy.h"
void do_some_job1 (void) {
// job 1
}
void do_some_job2 (void) {
// job 2
}
int main (void) {
jf_link_setfreq ((jf_setfreq_pt)JF_setfreq); // link with driver
jf_link_value ((jiffy_t*)&JF_TIM_VALUE);
jf_init (1000000, 1000); // 1MHz timer, 1000 counts, 1 usec per count
// use delay version
do_some_job1 ();
jf_delay_us (300); // wait for at least 300 usec
do_some_job1 ();
// use polling version
do_some_job1 ();
while (jf_check_usec (300)) {
do_some_job2 (); // keep calling for at least 300 usec
}
}

GPIO interrupt for different pins in PSoC 1

I have faced a problem connected with GPIO interrupt.
The task is to make a simple UI interface, so I need to use 3 buttons.
The problem is that I don't understand how to use GPIO interrupt for different pins and all my buttons work the same way.
here is the code:
#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int value; // the actual value which is used in the module
char string[16]; // string that is printed in LCD for user
} UI_ELEMENT;
#define FIRST_LEVEL 3
#define SECOND_LEVEL 3
#define PWM 0
#define PGA 1
#define ADC 2
#define PWM_STATE 0
#define PWM_PERIOD 1
#define PWM_WIDTH 2
#define PWM_STATE_OFF 0
#define PWM_STATE_ON 1
volatile int buttonRightPressed = 0;
#pragma interrupt_handler buttonRightInt
void buttonRightInt(void){
// disable button interrupt
M8C_DisableIntMask(INT_MSK0, INT_MSK0_GPIO);
buttonRightPressed = 1;
}
void initialize_LCD(void){
LCD_Position(0,0);
LCD_PrCString("PWM");
LCD_Position(1,0);
LCD_PrCString("< select >");
}
void update_LCD(int* lvl1){
if (*lvl1 == PWM || *lvl1 == 3){
LCD_Position(0,0);
LCD_PrCString("PWM");
*lvl1 = 0;
}
else if (*lvl1 == PGA){
LCD_Position(0,0);
LCD_PrCString("PGA");
}
else if (*lvl1 == ADC){
LCD_Position(0,0);
LCD_PrCString("ADC");
}
}
void main(void)
{
UI_ELEMENT userInterface[FIRST_LEVEL][SECOND_LEVEL];
int level_1_steper = PWM;
int i;
M8C_EnableGInt ; // Uncomment this line to enable Global Interrupts
PWM8_EnableInt();
LCD_Start();
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
initialize_LCD(); // set 'PWM' for upper row, '< select >' for lower row
while (1){
if (buttonRightPressed == 1){
for ( i = 0; i < 350; i++);
level_1_steper++;
update_LCD(&level_1_steper);
buttonRightPressed = 0;
// enable button interrupt again
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
}
}
}
Problem resolved! As usually solution is quite simple: use GPIO interrupt but test which button has been pressed. GPIO iterrupt:
void buttonInt(void){ // disable button interrupt
M8C_DisableIntMask(INT_MSK0, INT_MSK0_GPIO);
if (Right_Data_ADDR & Right_MASK) buttonRightPressed = 1;
if (Left_Data_ADDR & Left_MASK) buttonLeftPressed = 1;
if (Select_Data_ADDR & Select_MASK) buttonSelectPressed = 1;
}

"Grouping" enum values in C

If I had some enums like
typedef enum {
AN_TRISTATE_0,
AN_TRISTATE_1,
AN_NOTHING,
AN_MOTOR_1,
AN_MOTOR_2,
AN_MOTOR_3,
AN_SENSOR_1,
AN_SENSOR_2,
AN_SENSOR_3,
AN_SENSOR_4,
AN_SENSOR_5
} adc_pin_func_t;
and
adc_pin_func_t a_particular_pin = ...
, would it be possible it check if the pin is part of a particular group, e.g pin is part of AN_MOTOR or part of AN_SENSOR, instead of having to check against each item in each possible group.
Or are there more efficient ways of doing this, other than using enums?
Thanks in advance
You could create masks for each of the groups:
typedef enum {
AN_TRISTATE_0 = 0x00001,
AN_TRISTATE_1 = 0x00002,
AN_TRISTATE_MASK = 0x0000f,
AN_NOTHING = 0x00010, // Should this be 0x00000 ?
AN_MOTOR_1 = 0x00100,
AN_MOTOR_2 = 0x00200,
AN_MOTOR_3 = 0x00400,
AN_MOTOR_MASK = 0x00f00,
AN_SENSOR_1 = 0x01000,
AN_SENSOR_2 = 0x02000,
AN_SENSOR_3 = 0x04000,
AN_SENSOR_4 = 0x08000,
AN_SENSOR_5 = 0x10000,
AN_SENSOR_MASK = 0xff000
} adc_pin_func_t;
And then simply test a group against the mask using the & operator:
if (a_particular_pin & AN_SENSOR_MASK)
{
// it's a sensor pin
}
else if (a_particular_pin & AN_MOTOR_MASK)
{
// it's a motor pin
}
EDIT: As others have suggested using a range, then you could probably create a macro for the test, which would allow you to change how the test is performed without the need to change the code (always a good thing):
#define IS_AN_SENSOR(x) (((x) & AN_SENSOR_MASK) != 0)
#define IS_AN_MOTOR(x) (((x) & AN_MOTOR_MASK) != 0)
// etc.
and then the test becomes:
if (IS_AN_SENSOR(a_particular_pin))
{
// it's a sensor pin
}
else if (IS_AN_MOTOR(a_particular_pin))
{
// it's a motor pin
}
// etc
If you then needed to change to using a range then only the macros need to change (and you'd obviously need to define the range min/max):
#define IS_AN_SENSOR(x) ((x) >= AN_SENSOR_START && (x) <= AN_SENSOR_END)
// etc
You are free to choose your enum values, so you could do something like this
typedef enum {
AN_TRISTATE_0 = 0x0001,
AN_TRISTATE_1 = 0x0002,
AN_NOTHING = 0x0000,
AN_MOTOR_1 = 0x0010,
AN_MOTOR_2 = 0x0020,
AN_MOTOR_3 = 0x0030,
AN_SENSOR_1 = 0x0100,
AN_SENSOR_2 = 0x0200,
AN_SENSOR_3, /*and so on*/
AN_SENSOR_4,
AN_SENSOR_5
} adc_pin_func_t;
Then you can compare bits to check categories. For example, a motor type is the only category that will have non-zero (AN_MOTOR_2 & 0x00F0)
You can do a
typedef enum {
AN_TRISTATE_START,
AN_TRISTATE_0 = AN_TRISTATE_START,
AN_TRISTATE_1,
AN_TRISTATE_END = AN_TRISTATE_1,
AN_NOTHING,
AN_MOTOR_START,
AN_MOTOR_1 = AN_MOTOR_START,
AN_MOTOR_2,
AN_MOTOR_3,
AN_MOTOR_END = AN_MOTOR_3,
AN_SENSOR_START,
AN_SENSOR_1 = AN_SENSOR_START,
AN_SENSOR_2,
AN_SENSOR_3,
AN_SENSOR_4,
AN_SENSOR_5,
AN_SENSOR_END = AN_SENSOR_5
} adc_pin_func_t;
bool inline
is_sensor(int pin)
{
return AN_SENSOR_START <= pin
&& pin <= AN_SENSOR_END
}
and then in your code
if ( is_sensor(pin) )
{
/* body */
}
This way you don't have to care about masking particular values. May be useful if groups contain a lot of values.
You can give values to the enum in exponents of 2. Then you can simply use bitwise AND and OR masks. So you can assign values like 1,2,4,8,16,32... so on.
typedef enum {
AN_TRISTATE_0 = 1,
AN_TRISTATE_1 = 2,
AN_NOTHING = 4,
AN_MOTOR_1 = 8,
AN_MOTOR_2 = 16,
AN_MOTOR_3 = 32,
AN_SENSOR_1 = 64,
AN_SENSOR_2 = 128,
AN_SENSOR_3 = 256,
AN_SENSOR_4 = 512,
AN_SENSOR_5 = 1024
} adc_pin_func_t;
Then for checking with motor type, you can AND with (32+16+8) = 56. So pin & 56, if non zero will mean it is of motor type.
If you really like to have some modularity in your enum (the "hard coded" enum values are also a valid method), you can implement a struct with some OOP flavour. The design become more complicated, but the usage is still simple :
#include <stdio.h>
#include <string.h>
// Every enum will have its first value start at the value of the previous enum's last member PLUS ONE
#define OFFSET_ENUM_MOTOR ( sizeof(adc_pin_tristate_t) )
#define OFFSET_ENUM_SENSOR ( OFFSET_ENUM_MOTOR + sizeof(adc_pin_motor_t) )
///////////////////////////////////////////////////////////////////////////////
// Enum
typedef enum {
AN_TRISTATE_0,
AN_TRISTATE_1,
AN_NOTHING
} adc_pin_tristate_t;
typedef enum {
AN_MOTOR_1 = OFFSET_ENUM_MOTOR,
AN_MOTOR_2,
AN_MOTOR_3
} adc_pin_motor_t;
typedef enum {
AN_SENSOR_1 = OFFSET_ENUM_SENSOR,
AN_SENSOR_2,
AN_SENSOR_3,
AN_SENSOR_4,
AN_SENSOR_5
} adc_pin_sensor_t;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Struct for abstraction
typedef struct adc_pin_func2_t{
// our "enum value"
unsigned int enum_id;
// return is the enum is a motor one
int(*isMotor)(struct adc_pin_func2_t*);
} adc_pin_func2_t;
// Struct
///////////////////////////////////////////////////////////////////////////////
// Member methods : return if the enum is a motor one
int
PinFunc_isMotor(
adc_pin_func2_t *This /* object */
)
{
return ( (This->enum_id>=OFFSET_ENUM_MOTOR) && (This->enum_id<OFFSET_ENUM_SENSOR) );
}
// Creation of the structure
// Initialization
static void
PinFunc_Init(
adc_pin_func2_t *This, /* output */
unsigned int identifier /* old enum identifier */
)
{
// copy members
This->enum_id = identifier;
//copy methods (do not forget to do it !)
This->isMotor = PinFunc_isMotor;
}
// Constructor
adc_pin_func2_t
PinFunc_Create(
unsigned int identifier /* old enum identifier */
)
{
adc_pin_func2_t This;
PinFunc_Init(&This, identifier);
return This;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
main()
{
adc_pin_func2_t pin = PinFunc_Create(AN_NOTHING);
printf("%d \n", pin );
printf("%d \n", pin.isMotor(&pin) );
adc_pin_func2_t pin2 = PinFunc_Create(AN_MOTOR_2);
printf("%d \n", pin2 );
printf("%d \n", pin2.isMotor(&pin2) );
}
The usage of members functions like pin.isMotor(&pin) isn't very elegant (we repeat pin), but it is a shortcoming of C, which is not an OOP language.
If you don't want to have to manually maintain non-overlapping values, then you can very nearly get it all handled for you automatically. The only thing you'll have to figure out is the maximum number of bits in a group:
#define PIN_GROUP_SHIFT 4
#define GET_PIN_GROUP(x) (adc_pin_group_t)((y) >> PIN_GROUP_SHIFT)
#define PIN_GROUP_START(x) XX_GROUP_##x = ((GROUP_##x << PIN_GROUP_SHIFT) - 1),
enum {
GROUP_TRISTATE,
GROUP_NOTHING,
GROUP_MOTOR,
GROUP_SENSOR
} adc_pin_group_t;
typedef enum {
PIN_GROUP_START(TRISTATE)
AN_TRISTATE_0,
AN_TRISTATE_1,
PIN_GROUP_START(NOTHING)
AN_NOTHING,
PIN_GROUP_START(MOTOR)
AN_MOTOR_1,
AN_MOTOR_2,
AN_MOTOR_3,
PIN_GROUP_START(SENSOR)
AN_SENSOR_1,
AN_SENSOR_2,
AN_SENSOR_3,
AN_SENSOR_4,
AN_SENSOR_5
} adc_pin_func_t;
To determine the type of an entry in the enum, use GET_PIN_GROUP(x), and compare it to whichever value of the adc_pin_group_t enum. You can even switch on the result, if that's helpful.
However, that AN_NOTHING entry makes me wonder if your enum is meant to line up with specific values for each entry. are specific values associated with the pins, which you may not be able to assign arbitrarily. In that case you might need to try something complicated (which I haven't tested):
#define GET_PIN_VALUE(x) ((x) & ((1 << PIN_GROUP_SHIFT) - 1)
#define PIN_GROUP_START(x) \
WW_GROUP_##x, \
XX_GROUP_##x = (GROUP_##x << PIN_GROUP_SHIFT) \
+ GET_PIN_INDEX(WW_GROUP_##x) - 1,
Where you need to know the value that your original enum would have used, use GET_PIN_INDEX(x).

Resources