You could share the I2C bus by using a semaphore. Each thread that uses the I2C bus has this form.
// Declare and initialize a semaphore for limiting access to a region.
SEMAPHORE_DECL(i2cSem, 1);
// Time between acceses.
const systime_t PERIOD_ONE_TICKS = 123; // set value for your case!
//------------------------------------------------------------------------------
// Declare the thread function.
NIL_THREAD(thdFcn, arg) {
// Time for next read.
systime_t wakeTime = nilTimeNow();
while (true) {
wakeTime += PERIOD_ONE_TICKS;
// Sleep until time for next bus access.
nilThdSleepUntil(wakeTime);
// Wait for access to I2C bus.
nilSemWait(&i2cSem);
// Access I2C bus here.
// Release I2C bus.
nilSemSignal(&i2cSem);
}
}
Another option is to access one of the devices with software I2C.
Could be the setsyncProvider() with setSyncInterval() used in setup() with Nirtos? Or better, a task shall be used for syncing (instead of the setSyncInterval())?
setSyncProvider(RTC.get); // the function to get the time from the actual RTC
..
setSyncInterval(300); // set the number of seconds between re-sync of time
nilSysBegin();
It comes from Time.h
PS: setSyncProvider() calls RTC in regular intervals in order to sync "local time" (based on millis(). So it seems it must be guarded by a mutex.
You just need to define a protected function to return the time. All access to I2C must be protected by the same semaphore.
time_t safeGetTime() {
time_t t;
nilSemWait(&i2cSem);
t = RTC.get();
nilSemSignal(&i2cSem);
return t;
}
...
setSyncProvider(safeGetTime); // the function to get the time from the actual RTC
I did it with a periodic task. Yours is more resource friendly, of course
while (TRUE) {
wakeTime += SYSTIME_SYNC_PERIOD_TICKS;
// Sleep until time for next data point
nilThdSleepUntil(wakeTime);
// Read RTC..
nilSemWait(&i2cfree);
new_time = rtc.get_tm();
nilSemSignal(&i2cfree);
// .. and sync system Time and Date
setTime(new_time);
now(); // this makes the sync
}
}
Q: can I work with semaphores before I start the NilRtos? Like:
..
// SYNC the RTC and systime at boot
setSyncProvider(safeGetTime); // the function to get the time from RTC
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
setSyncInterval(300); // set the number of seconds between re-sync of time
sys_t_start = now(); // keep the boot start time
..
nilSysBegin();
While running above I get a proper time into "sys_t_start" var - it means the setSyncProvider() has called the safeGetTime() already.. So it had worked with semaphores, before the NilRtos actually started..
You will get away with calling safeGetTime before starting Nil since there will be no wait on the semaphore. nilSemWait will decrement the count to zero and return.
I've installed SdFat's demos (Bench and SdInfo) into my Nil experimental setup. The issue is they use SdFat and Sd2Card respectively. So to put them together work with
SdFat sd;
Sd2Card card;
only, I was not able to merge the stuff such it uses only one class for the card. And it works (very surprised though) under Nil even under "heavy" load (several periodic tasks run in bg) ;).
This is how I did the error states handling inside a thread. The thread starts based on a signal from CLI. Is there any better option how to handle the error states within a Nilrtos thread?
// Declare the thread function
NIL_THREAD(SdBench, arg) {
while (TRUE) {
// Wait for signal from CLI
nilSemWait(&sdbench);
// initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
error("\nsd.init failed");
goto thrdend1;
}
..
// open or create file - truncate existing file.
if (!file.open("BENCH.DAT", O_CREAT | O_TRUNC | O_RDWR)) {
error("open failed");
goto thrdend1;
}
..
for (uint32_t i = 0; i < n; i++) {
uint32_t m = micros();
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
error("write failed");
goto thrdend1;
}
..
for (uint32_t i = 0; i < n; i++) {
buf[BUF_SIZE-1] = 0;
uint32_t m = micros();
if (file.read(buf, sizeof(buf)) != sizeof(buf)) {
error("read failed");
goto thrdend1;
}
..
if (buf[BUF_SIZE-1] != '\n') {
error("data check");
goto thrdend1;
}
..
thrdend1: file.close();
} // This is the While ()
} // This is the NIL_THREAD()
Looks like you are going beyond the design thread count for Nil. I removed this when I made the thread count dynamic.
#if NIL_CFG_NUM_THREADS > 12
#error "Nil is not recommended for thread-intensive applications, consider" \
"ChibiOS/RT instead"
#endif
Nil uses very simple algorithms for scheduling that involve scanning tables. ChibiOS uses more conventional linked lists to order objects for more efficiency when there are many objects. This also allows dynamic priorities and priority inheritance for a mutex.
Is there any better option how to handle the error states within a Nilrtos thread?
Alarms, events, and logging are major components of large embedded systems. Usually there is an API for threads to communicate with this module using something like mail boxes. Error messages are logged and displayed for operators. Operators can acknowledge errors and reply to threads.
Here are two links that illustrate the scale of this subsystem.
A counting semaphore is a synchronization object that can have an arbitrarily large number of states. The internal state is defined by a signed integer variable, the counter. The counter value (N) has a precise meaning:
Negative, there are exactly -N threads queued on the semaphore.
Zero, no waiting threads, a wait operation would put in queue the invoking thread.
Positive, no waiting threads, a wait operation would not put in queue the invoking thread.
Two operations are defined for counting semaphores:
Wait (chSemWait() in ChibiOS/RT). This operation decreases the semaphore counter, if the result is negative then the invoking thread is queued.
Signal (chSemSignal() in ChibiOS/RT). This operation increases the semaphore counter, if the result is non-negative then a waiting thread is removed from the queue and resumed.
There are some updates related to Nilrtos at chibios.org - do you plan an update too?
I plan to port the new version. This will actually be a large change.
the convergence process between Nil (now ChibiOS/NIL) and ChibiOS/RT is almost complete. Nil gained events and the API now is almost equal to the ChibiOS/RT API.
Now Nil has been integrated in the ChibiOS 3.0 development branch and is able to use the full HAL, the very same HAL used by ChibiOS/RT.
Starting from ChibiOS 3.0 Nil will be included in the same releases of the other products:
ChibiOS/RT
ChibiOS/NIL
ChibiOS/HAL
Added to Nil:
Support for stack checking.
Events.
Port to M3/M4.
Simplified task declaration macros.
Demos for F0/F3 Discovery boards.
Strong commonality with ChibiOS/RT API and ports (very few differences in port files).
I will be be away for the last two weeks in September so I will start work on the new Nil in October.
Great work fat16lib. I'm building home security system based on 644P and I hope I can create the software on top of NIl. I will be using serial 485 communication between nodes, 8 ADC, 6 Digital, GSM SMS, maybe ethernet, radio ... long run :).
Now and than I try your examples and also read the forum here. So far I'm getting away from my mistakes and not very good C. But what bothers me, is some of my threads stop working after some time and unused stack of other threads suddenly goes to 0. Do you have some advice?
If i want to use Nil RTOS, all I have to do is copy the NilAnalog, NilRTOS, NilTimer1, and
TwiMaster folders to your Arduino/libraries folder as stated in the readme right? Just want to make sure I don't have to download any .exe file to implement this.