1. LEDs
To confirm this part has no problem, in BlinkC I just toggle the led0 status like this way:
event void Boot.booted()
{
call Leds.led0Toggle();
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}
Compiling and run it, the led0 toggle! So LEDs should be OK! Deep investigation on this part eliminated!
2. Timer
Now should be the Timer part, this one is the key part to motivate the LEDs blink!
Let’s trace it from Application point of view.
2.1 Application
module BlinkC
{
uses interface Timer<TMilli> as Timer0;
uses interface Timer<TMilli> as Timer1;
uses interface Timer<TMilli> as Timer2;
uses interface Leds;
uses interface Boot;
}
implementation
{
event void Boot.booted()
{
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}
}
2.2 Timer.nc in Timer Library
interface Timer<precision_tag>
{
command void startPeriodic(uint32_t dt);
““““““`
}
2.3 Platform Side
2.3.1 HilTimerMilliC
configuration HilTimerMilliC {
provides interface Init;
provides interface Timer<TMilli> as TimerMilli[uint8_t num];
provides interface LocalTime<TMilli>;
}
implementation {
Init = AlarmCounterMilliP;
“““““`
TimerMilli = VirtualizeTimerC;
VirtualizeTimerC.TimerFrom -> AlarmToTimerC;
AlarmToTimerC.Alarm -> AlarmCounterMilliP;
LocalTime = CounterToLocalTimeC;
CounterToLocalTimeC.Counter -> AlarmCounterMilliP;
}
2.3.1.1 VirtualizeTimerC
generic module VirtualizeTimerC(typedef precision_tag, int max_timers)
{
provides interface Timer<precision_tag> as Timer[uint8_t num];
uses interface Timer<precision_tag> as TimerFrom;
}
implementation
{
void startTimer(uint8_t num, uint32_t t0, uint32_t dt, bool isoneshot)
{
Timer_t* timer = &m_timers[num];
timer->t0 = t0;
timer->dt = dt;
timer->isoneshot = isoneshot;
timer->isrunning = TRUE;
post updateFromTimer();
}
command void Timer.startPeriodic[uint8_t num](uint32_t dt)
{
startTimer(num, call TimerFrom.getNow(), dt, FALSE);
}
“““`
}
2.3.1.2 AlarmToTimerC
generic module AlarmToTimerC(typedef precision_tag)
{
provides interface Timer<precision_tag>;
uses interface Alarm<precision_tag,uint32_t>;
}
implementation
{
// there might be ways to save bytes here, but I’ll do it in the obviously
// right way for now
uint32_t m_dt;
bool m_oneshot;
void start(uint32_t t0, uint32_t dt, bool oneshot)
{
m_dt = dt;
m_oneshot = oneshot;
call Alarm.startAt(t0, dt);
}
command void Timer.startPeriodic(uint32_t dt)
{ start(call Alarm.getNow(), dt, FALSE); }
command void Timer.startOneShot(uint32_t dt)
{ start(call Alarm.getNow(), dt, TRUE); }
command void Timer.stop()
{ call Alarm.stop(); }
task void fired()
{
if(m_oneshot == FALSE)
start(call Alarm.getAlarm(), m_dt, FALSE);
signal Timer.fired();
}
async event void Alarm.fired()
{ post fired(); }
command bool Timer.isRunning()
{ return call Alarm.isRunning(); }
command bool Timer.isOneShot()
{ return m_oneshot; }
command void Timer.startPeriodicAt(uint32_t t0, uint32_t dt)
{ start(t0, dt, FALSE); }
command void Timer.startOneShotAt(uint32_t t0, uint32_t dt)
{ start(t0, dt, TRUE); }
command uint32_t Timer.getNow()
{ return call Alarm.getNow(); }
command uint32_t Timer.gett0()
{ return call Alarm.getAlarm() – m_dt; }
command uint32_t Timer.getdt()
{ return m_dt; }
}
2.3.2 AlarmCounterMilliP
configuration AlarmCounterMilliP
{
provides interface Init;
provides interface Alarm<TMilli, uint32_t> as AlarmMilli32;
provides interface Counter<TMilli, uint32_t> as CounterMilli32;
}
implementation
{
components new Nios2AlarmAsyncC(TMilli, 32);//ATM128_CLK8_DIVIDE_32
Init = Nios2AlarmAsyncC;
AlarmMilli32 = Nios2AlarmAsyncC;
CounterMilli32 = Nios2AlarmAsyncC;
}
2.4 Chip Side
2.4.1 Nios2AlarmAsyncC
generic configuration Nios2AlarmAsyncC(typedef precision, int divider) {
provides {
interface Init @atleastonce();
interface Alarm<precision, uint32_t>;
interface Counter<precision, uint32_t>;
}
}
implementation
{
components new Nios2AlarmAsyncP(precision, divider),
HplNios2Timer0AsyncC;
Init = Nios2AlarmAsyncP;
Alarm = Nios2AlarmAsyncP;
Counter = Nios2AlarmAsyncP;
Nios2AlarmAsyncP.Timer -> HplNios2Timer0AsyncC;
Nios2AlarmAsyncP.Compare -> HplNios2Timer0AsyncC;
}
2.4.2 Nios2AlarmAsyncP
generic module Nios2AlarmAsyncP(typedef precision, int divider) {
provides {
interface Init;
interface Alarm<precision, uint32_t>;
interface Counter<precision, uint32_t>;
}
uses {
interface HplNios2Timer<uint32_t> as Timer;
interface HplNios2Compare<uint32_t> as Compare;
}
}
implementation
{
““`
/* Configure timer 0 */
command error_t Init.init() {
atomic
{
call Compare.set(alt_ticks_per_second()); /* setInterrupt needs a valid value here */
call Compare.start();
}
setInterrupt();
return SUCCESS;
}
async command uint32_t Counter.get() {
uint32_t now;
atomic
{
now = call Timer.get();
}
return now;
}
async command void Alarm.start(uint32_t ndt) {
call Alarm.startAt(call Counter.get(), ndt);
}
async command void Alarm.startAt(uint32_t nt0, uint32_t ndt) {
atomic
{
set = TRUE;
t0 = nt0;
dt = ndt;
}
setInterrupt();
}
async command uint32_t Alarm.getNow() {
return call Counter.get();
}
async command uint32_t Alarm.getAlarm() {
atomic return t0 + dt;
}
“““““`
}
2.4.3 HplNios2Timer0AsyncC
configuration HplNios2Timer0AsyncC
{
provides {
interface HplNios2Timer<uint32_t> as Timer;
interface HplNios2Compare<uint32_t> as Compare;
}
}
implementation
{
components HplNios2Timer0AsyncP;
components McuSleepC;
McuSleepC.McuPowerOverride -> HplNios2Timer0AsyncP;
Timer = HplNios2Timer0AsyncP;
Compare = HplNios2Timer0AsyncP;
}
2.4.4 HplNios2Timer0AsyncP
module HplNios2Timer0AsyncP {
provides {
interface HplNios2Timer<uint32_t> as Timer;
interface HplNios2Compare<uint32_t> as Compare;
interface McuPowerOverride;
}
}
3. Visualizing a Component Graph
Seems a little difficult to trace using only code trace. So I use graphviz to generate component graph, it’s really a excellent tool to develop for TinyOS. Now I try to post related wirings as followings.
3.1 Component: BlinkAppC
3.2 Component: tos.system.TimerMilliC
3.3 Component: tos.system.TimerMilliP
3.4 Component: ……tos.platforms.toni.HilTimerMilliC
3.5 Component: ……tos.platforms.toni.AlarmCounterMilliP
3.6 Component: tos.chips.nios2.timer.Nios2AlarmAsyncC
3.7 Component: tos.chips.nios2.timer.HplNios2Timer0AsyncC
4. Implementation
For functions being used for Blink application are
TimerFrom.getNow(), TimerFrom.stop(), Alarm.getNow(), Alarm.startAt(t0, dt), Alarm.stop(), Alarm.fired(), Alarm.getAlarm();
event void TimerFrom.fired()
{
fireTimers(call TimerFrom.getNow());
}
task void fired()
{
if(m_oneshot == FALSE)
start(call Alarm.getAlarm(), m_dt, FALSE);
signal Timer.fired();
}
async event void Alarm.fired()
{ post fired(); }
So the real implementation for NiosII is Atm128AlarmAsyncP and HplNios2Timer0AsyncP, which are we gonna explore and compare with related chipes.
4.1 Atm128AlarmAsyncP
module HplNios2Timer0AsyncP {
provides {
interface HplNios2Timer<uint32_t> as Timer;
interface HplNios2Compare<uint32_t> as Compare;
interface McuPowerOverride;
}
}
implementation
{
uint32_t i_ticks = 0;
/* The alt_alarm must persist for the duration of the alarm. */
static alt_alarm alarm;
//=== Read the current timer value. ===================================
async command uint32_t Timer.get() {
struct timeval time;
struct timezone zone;
if(gettimeofday(&time, &zone) == 0)
{
return time.tv_usec;
}
return 0; }
//=== Set/clear the current timer value. ==============================
async command void Timer.set(uint32_t t) {
struct timeval time;
struct timezone zone;
if(gettimeofday(&time, &zone) == 0)
{
time.tv_usec = 0;
settimeofday(&time, &zone);
}
else
{
zone.tz_minuteswest = 0;
zone.tz_dsttime = 0;
time.tv_sec = 0;
time.tv_usec = 0;
settimeofday(&time, &zone);
}
}
//=== Read the current timer scale. ===================================
async command uint8_t Timer.getScale() { return alt_ticks_per_second(); }
//=== Turn off the timers. ============================================
async command void Timer.off() {
//call Timer.setScale(AVR_CLOCK_OFF);
}
//=== Write a new timer scale. ========================================
async command void Timer.setScale(uint8_t s) {
//Nios2TimerControl_t x = call TimerCtrl.getControl();
//x.bits.cs = s;
//call TimerCtrl.setControl(x);
}
//=== Timer 8-bit implementation. ====================================
async command void Timer.reset() {
//TIFR = 1 << TOV0;
}
async command void Timer.start() {
//SET_BIT(TIMSK, TOIE0);
}
async command void Timer.stop() {
//CLR_BIT(TIMSK, TOIE0);
}
bool overflowed() {
//return (call TimerCtrl.getInterruptFlag()).bits.tov0;
return 0;
}
async command bool Timer.test() {
//return overflowed();
return 0;
}
async command bool Timer.isOn() {
//return (call TimerCtrl.getInterruptMask()).bits.toie0;
return 1;
}
async command void Compare.reset() {
//TIFR = 1 << OCF0;
}
async command void Compare.stop() {
//CLR_BIT(TIMSK,OCIE0);
}
async command bool Compare.test() {
//return (call TimerCtrl.getInterruptFlag()).bits.ocf0;
return 1;
}
async command bool Compare.isOn() {
//return (call TimerCtrl.getInterruptMask()).bits.ocie0;
return 1;
}
//=== Read the compare registers. =====================================
async command uint32_t Compare.get() { return i_ticks; }
//=== Timer interrupts signals ========================================
inline void stabiliseTimer0() {
//TCCR0 = TCCR0;
//while (ASSR & 1 << TCR0UB)
// ;
}
/**
* On the atm128, there is a small latency when waking up from
* POWER_SAVE mode. So if a timer is going to go off very soon, it’s
* better to drop down until EXT_STANDBY, which has a 6 cycle wakeup
* latency. This function calculates whether staying in EXT_STANDBY
* is needed. If the timer is not running it returns POWER_DOWN.
* Please refer to TEP 112 and the atm128 datasheet for details.
*/
async command mcu_power_t McuPowerOverride.lowestState() { }
default async event void Compare.fired() { }
uint32_t FIRED_HANDLER(void* context) {
signal Compare.fired();
return i_ticks;
}
default async event void Timer.overflow() { }
//=== Write the compare registers. ====================================
async command void Compare.set(uint32_t t) {
i_ticks = t;
}
async command void Compare.start() {
alt_alarm_start(&alarm, i_ticks, FIRED_HANDLER, NULL);
}
}
5. Debug directly in NiosII IDE
Cazu app.c is generated, and we need to compile it in NiosII IDE, so we could trace directly in NiosII IDE, by changing some source code or print something, we could figure some errors out.
a. alt_alarm_start could be invoked, by printf.
b. But, the interrupt could not be invoked:
b.1 only use one timer to test, the Fire handler could be invoked once now, and also the Blink
event void Timer0.fired() could be invoked, cauz the Led0 could be toggled, but not always work.
b.2 The reason that the alarm could not fire is that invoke alt_alarm_start() twice with the same static alt_alarm alarm; which only support one alt_alarm_start() each. But now could not invoke led toggole.
b.3 So the signal defenitely is wrong, trace signal path from application part:
VirtualizeTimerC$0$Timer$fired(uint8_t arg_0x1a9841d0){
switch (arg_0x1a9841d0) {
#line 72
case 0U:
}
VirtualizeTimerC$0$fireTimers(uint32_t now){
“““
VirtualizeTimerC$0$Timer$fired(num);
}
VirtualizeTimerC$0$updateFromTimer$runTask(void)
{
“`VirtualizeTimerC$0$fireTimers(now);
}
SchedulerBasicP$TaskBasic$runTask(uint8_t arg_0x1a520010){ VirtualizeTimerC$0$updateFromTimer$runTask();}
So, it actually runs as a task, so the task scheduler runs on timer, this path seems not a good option to trace.
c. Thanks for Siew Kiat and Wei Chiet coming, your lucky boys, haha, now multiply timers could work, but still have some problems, which are Leds could not toggle properly and Timer0 will stop after a while.
The reason for Leds could not toggle is that although we could write registers to Leds, but when read the registers, the value could not be read correctly!
I think it’s for Leds were set output only, so compile soft-core again, and have a try.
Now set Leds bidirection,
Solved!
Now the red led could blink!
d. After running for a while, the timer would stop.
I think it’s the overflow problem of timer, tracing it now.
Yeah, after tracing and debugging, it is the problem of overflow.
I extended the limitation of timer into 32 bit in HplNios2Timer0AsyncP component, now it could work correctly!
After working a whole night, it still blinks, quite well…then
Next Step, UART Porting!