Jump to content
neptun

Porting Fanspeed Example To Pic16 - Stucked..

Recommended Posts

neptun    0

Hi out there,

I'm trying to get familiar with Novo and started therefore with the FanSpeed example of Dave.

I got it to work without big problems on a PIC18F2550 and an older PICDEM-2 demo board.

Cause I had a 4x4-keypad in my corner I extended the keypad-file. It all worked fine.

As next step I wanted to get it worked also on a PIC16 and have chosen a PICkit2 demo board with a PIC16F690.

Same setup there but running with an external crystal at 20MHz. Additionally I changed the scheduler-call SysTimerUpdate from main to a timer-driven interrupt that should execute every 10us.

 

I dunno what I did wrong but the code doesn't work.

1st issue: the keypad didn't get read.

2nd issue: after a few seconds the fan gets set to max. speed what is probably caused by a dead-lock or sth. like that.

 

I attached an UART-to-RS232 converter but just got the information so far that the program stucks after several executions in task1...

 

Find the code attached. I hope someone could give me a good hint... :)

 

Thanks in advance!

 

 

>> PicConfig.h <<

// PIC 16F690 with external oscillator (crystal) 20 MHz
#pragma DATA _CONFIG, _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & \
_MCLRE_ON & _PWRTE_ON & _WDT_OFF  & _HS_OSC

 

>> fanspeed.c <<

//////////////////////////////////////////////
// Fan Speed Controller
//					  
// Author(s): David Hobday
// Date: 11 December 2006
//
// Target Device: EZ-Controller Turbo (PIC18F2520 with booloader)
//
// Revision History:
// V1.0 11/12/2006 - Initial Release
//
//////////////////////////////////////////////
//
// This code controls the speed of a fan using pulse width modulation,
// ie the fan speed is controlled by varying the duty cycle of voltage
// waveform applied to the fan - all done without using any PWM hardware.
//
// The desired speed is set by typing a 1 to 3 digit number (0 to 255) on the
// keypad and then pressing the '#' key. Pressing the '*' key switches
// between the last two set speeds.
//
// This code uses Novo RTOS as it makes things so much simpler.
// The PWM is implemented in software using Novo RTOS Sys_Sleep functions.
//
//============================================================================
// My adaptions:
//----------------------------------------------------------------------------
// A Keypad matrix is connected to the µC (see keypad.c for details).
// A pwm output is genrated from µC-pin in "bit-banging"-style.
//
// The Novo SysTimerUpdate() function is called at timer0-interrupt of 10µs, so any
// time values used with waiting functions, such as Sys_Sleep(), represent the
// number of times the run queue is transversed there.
// The setup is:
// - PICkit2-EvalBoard with PIC16F690
// - external oscillator (crystal) of 20MHz
// - used 4x4-keypad and extended "keypad.c" respectively
// - attached UART-to-RS232 converter and added some debug-output
//
#include <system.h>
#include <novocfg_pic16t3e5ts1.h>
#include <novo.h>
// use this configuration when not targeting EZ-COntroller turbo with bootloader,
// also remember to remove linker option -rb 0x800
#include "PicConfig.h"
#pragma CLOCK_FREQ 20000000  //set to 20MHz at external crystal
#include "keypad.h"
#define hTask0 0
#define hTask1 1
unsigned char speed = 50; // set initial speed
// Delays are measured in run execution counts as we update SysTimerUpdate in the main yeild loop
// In this application an update occurs around every 10us
#define DEBOUNCE_DELAY_2500us() Sys_Sleep( 250 )
unsigned char WaitForKey()
{
unsigned char key = 0;
unsigned char i;

// wait for no key press and keypad stable for a number of loops
for( i = 0; i < 10; i++ )
{
 if( ScanKeyMatrix() != 0xFF )
  i = 0;
 DEBOUNCE_DELAY_2500us();
}

// wait for key pressed and stable for a number of loops
while( 1 )
{
 txreg = 'w'; //send to UART
 for( i = 0; i < 10; i++ )
 {
  unsigned char k = ScanKeyMatrix();
  if( key != k ) // key has changed the same
  {
   key = k;
   i = 0;
  }
  DEBOUNCE_DELAY_2500us(); // debounce delay
  txreg = key; //send key to UART
 }

 if( key != 0xFF ) // a key has been pressed
 {
  txreg = key; //send key to UART
  return key;
 }
}
}

void Task0()
{
ScanKeyMatrixInit();

char key;
unsigned char newSpeed = 0, oldSpeed = 0;
while( 1 )
{
 txreg = '0'; //send to UART
 key = WaitForKey();
 if( key >= '0' && key <= '9' )
 {  
  newSpeed *= 10;
  newSpeed += key - '0';
 }

 if( key == '#' )
 {
  oldSpeed = speed;
  speed = newSpeed;
  newSpeed = 0;
 }

 if( key == '*' )
 {
  newSpeed = speed;
  speed = oldSpeed;
  oldSpeed = newSpeed;
  newSpeed = 0;
 } 
 if( key == 'A' )
 {
  speed = 255;
 }
 if( key == 'B' )
 {
  speed = 200;
 }
 if( key == 'C' )
 {
  speed = 120;
 }
 if( key == 'D' )
 {
  speed = 50;
 }
}
}
void Task1()
{
while( 1 )
{
 txreg = '1'; //send to UART
 portc.5 = 0;
 Sys_Sleep( 255 - speed );
 portc.5 = 1;
 Sys_Sleep( speed );
}
}

void interrupt(void)
{
// DISABLE ALL/GLOBAL INTERRUPT SOURCES!
intcon.GIE = false;  // global interrupt disable

// handle all interrupts:
// 1) timer0-source:
if(intcon.T0IF == true)
{
 tmr0 = 0x32; // reload counting-registers for timer0
 intcon.T0IF = false; // reset flag
 SysTimerUpdate(); // timer0 is used as novo-timebase in timer0-interrupt
}
// 2) UART-source:
if ( pir1.RCIF == true ) // RX via UART occured?
{
 if( (rcsta.OERR == true) || (rcsta.FERR == true) ) // any RX-error occured?
 {
  rcsta.CREN = false;
  nop();
  rcsta.CREN = true; // should be better get reset within error-handling !!
  rcsta.OERR = false; // reset overrun-error flag
  rcsta.FERR = false; // reset framing-error flag
 }
 else
 {
  //ToDo:
  //buffer = rcreg; // read the character from the receive chBuffer
  //// rcreg = 0x00;  // delete RX-register
	    //boRxInterruptFlag = TRUE_d;  // set flag for main application
 }
}

// (RE-)ENABLE ALL/GLOBAL INTERRUPT SOURCES!
intcon.GIE = true;  // global interrupt enable
}
void main()
{
// disable analog input-channels
ansel = 0x00; 
anselh = 0x00;
// disable weak pull-ups of port A & B
wpua = 0x00;
wpub = 0x00;
// set I/O-directions
trisa = 0b11111111;
trisb = 0b01111111;// only RB7 is output (TX of UART)
trisc = 0b11011111;// only RC5 is output

// init timer0
option_reg.T0CS = false; // TMR0 Clock Source = Internal instruction cycle clock (FOSC/4)
// for prescaler 1:1 =>> need to assign to WDT
option_reg.PSA = true;
tmr0 = 0x32; // load timer0-value => at Fosc/4 with prescaler 1:1 => value = 50 (0x32) for 10us
intcon.T0IE = true;  // timer0 interrupt enable

// init UART
txsta.BRGH = true; // baudrate-generator => high register activated for 16-bit value
rcsta.SPEN = true; // enable serial port
baudctl.BRG16 = true; // enable 16-bit-mode of baudrate-generator
// => value for baudrate 19200 =>> 259
spbrg = 0b00000011;
spbrgh = 0b00000001;
// enable the asynchronous serial port by clearing bit SYNC, and setting bit SPEN
txsta.SYNC = false;
rcsta.SPEN = true;
pie1.RCIE = true; // enable RX-interrupts
txsta.TXEN = true; // enable transmission
rcsta.CREN = true; // enable reception

// init interrupt-control register
intcon.PEIE = true;  // enable peripherial interrupts
intcon.GIE = true;  // global interrupt enable

// init Novo
SysInit();
SysCreateTask( hTask0, 2, Task0 );
SysCreateTask( hTask1, 2, Task1 );
SysStartTask( hTask0 );
SysStartTask( hTask1 );

while( 1 )
{
 Sys_Yield();
}
}

 

>> keypad.c <<

//////////////////////////////////////////////
// Matrix Keypad Scanning Routine
//					  
// Author(s): David Hobday
// Date 11 December 2006
//
// Revision History:
// V1.0 11/12/2006 - Initial Release
// V1.1 130323 - adaption on 4x4 keypad
//
//////////////////////////////////////////////
//============================================================================
// My adaptions:
//----------------------------------------------------------------------------
// - used 4x4-keypad and extended "keypad.c" respectively
//
// Code written for keypad
// with key layout as below:
// COL1 COL2 COL3  COL4
//   1    2    3   A ROW 1
//   4    5    6   B ROW 2
//   7    8    9   C ROW 3
//   *    0    #   D ROW 4
//
#include <system.h>
// define keypad connections
// => ORIGINAL SETTING
/*volatile bit row1port @PORTC.1;
volatile bit row1tris @TRISC.1;
volatile bit row2port @PORTC.6;
volatile bit row2tris @TRISC.6;
volatile bit row3port @PORTC.5;
volatile bit row3tris @TRISC.5;
volatile bit row4port @PORTC.3;
volatile bit row4tris @TRISC.3;
volatile bit col1port @PORTC.2;
volatile bit col1tris @TRISC.2;
volatile bit col2port @PORTC.0;
volatile bit col2tris @TRISC.0;
volatile bit col3port @PORTC.4;
volatile bit col3tris @TRISC.4;*/

// => SETTING PICkit2 with PIC16F690 AND UART enabled (uses RB5 for RX)
volatile bit row1port @PORTC.0;
volatile bit row1tris @TRISC.0;
volatile bit row2port @PORTA.2;
volatile bit row2tris @TRISA.2;
volatile bit row3port @PORTA.1;
volatile bit row3tris @TRISA.1;
volatile bit row4port @PORTC.3;
volatile bit row4tris @TRISC.3;
volatile bit col1port @PORTB.6;
volatile bit col1tris @TRISB.6;
volatile bit col2port @PORTB.4;
volatile bit col2tris @TRISB.4;
volatile bit col3port @PORTC.2;
volatile bit col3tris @TRISC.2;
volatile bit col4port @PORTC.1;
volatile bit col4tris @TRISC.1;

rom char* keyPadMatrix =
{
'1','2','3','A',
'4','5','6','B',
'7','8','9','C',
'*','0','#','D',
0xFF
};
void ScanKeyMatrixInit()
{
// we scan the keypad by turning on the row outputs and
// reading the columns
row1tris = 0;
row2tris = 0;
row3tris = 0;
row4tris = 0;
col1tris = 1;
col2tris = 1;
col3tris = 1;
col4tris = 1;
}
char ScanKeyMatrix()
{
// This routine returns the first key found to be
// pressed during the scan.
char key = 0, row;

for( row = 0b00000001; row < 0b00010000; row <<= 1 )
{ 
 { // turn on row output
  row1port = row.0;
  row2port = row.1;
  row3port = row.2;
  row4port = row.3;
 }

 // read colums - break when key press detected
 if( col1port )
  break;
 key++;
 if( col2port )
  break;
 key++;
 if( col3port )
  break;
 key++;
 if( col4port )
  break;
 key++;
}
row1port = 0;
row2port = 0;
row3port = 0;
row4port = 0;

return keyPadMatrix[ key ];
}

Share this post


Link to post
Share on other sites
Dave    0

neptun,

 

What version of the BoostC compiler are you using?

 

Regards

Dave

Share this post


Link to post
Share on other sites
JorgeF    0

Hi

 

 

After a very quick review of your code, I don't know if I can give you a good hint, but for now I have one comment and one question.

 

comment:

You don't need, to disable/enable GIE in the "interrupt" function, the PIC hardware does it for you.

Disabling it at the begining of "interrupt" is unnecessary as it is already disabled.

Enabling it at the end can be a problem because an interrupt can be trigered before the "retfie" instruction, creating a reentrancy.

 

Question:

When is the "while(1)" loop of "WaitForKey" exiting if nobody presses a key?

 

 

I will have time for a more carefull review of your code by the end of the day. Maybe then I can give you some really usefull hints.

 

 

Best regards

Jorge

Edited by JorgeF

Share this post


Link to post
Share on other sites
neptun    0

Hi Dave,

 

I definitely forgot to mention the tool-setup... :mellow:

I'm using the full version of BoostC v7.11 with a Novo Source Code license.

 

Regards

neptun

Share this post


Link to post
Share on other sites
neptun    0

Hi Jorge,

 

thanks for the "quick" analysis.

I didn't know (obviously) about the "automatical" GIE-interrupt-handling by the PIC hardware. Good to know that.

Although I don't think that this is the origin of the probblem cause I used this in other projects with BoostC in the same way and no more in the future... ;-)

 

Question:

When is the "while(1)" loop of "WaitForKey" exiting if nobody presses a key?

 

It's the same like in Dave's example code. After the keypad with no pressed key is stable, the task should stay in the function waiting for a (new) stable key-press.

 

 

I will have time for a more carefull review of your code by the end of the day. Maybe then I can give you some really usefull hints.

 

That would be very nice. I'll try the code without the GIE-enable/disable probably tonight and will give feedback on the results.

 

Cheers

neptun

Share this post


Link to post
Share on other sites
Dave    0

neptun,

Hi out there,

I'm trying to get familiar with Novo and started therefore with the FanSpeed example of Dave.

I got it to work without big problems on a PIC18F2550 and an older PICDEM-2 demo board.

Cause I had a 4x4-keypad in my corner I extended the keypad-file. It all worked fine.

As next step I wanted to get it worked also on a PIC16 and have chosen a PICkit2 demo board with a PIC16F690.

Same setup there but running with an external crystal at 20MHz. Additionally I changed the scheduler-call SysTimerUpdate from main to a timer-driven interrupt that should execute every 10us.

 

I dunno what I did wrong but the code doesn't work.

1st issue: the keypad didn't get read.

2nd issue: after a few seconds the fan gets set to max. speed what is probably caused by a dead-lock or sth. like that.

I'm running your code under SourceBoost IDE and simulator and I see the problem where the code seems to stop functioning correctly after a while. Not sure what the cause is yet.

 

Regards

Dave

Share this post


Link to post
Share on other sites
Dave    0

I dunno what I did wrong but the code doesn't work.

2nd issue: after a few seconds the fan gets set to max. speed what is probably caused by a dead-lock or sth. like that.

 

I found an issue in the Novo code kernel.

Please replace the code in the function below in Novo.c, you will need to rebuild the Novo library you are using. There is a SourceBoost IDE project supplied for that as part of the installation.

 

void SysiAddToSleepQueue( TASK_HANDLE hTask, TICK_COUNT sleepTime )
{
// Add to wait queue at appropriate place for wakeup time
// Tasks at the front of the queue are due to wakeup before those at the rear.

// Add to sleep queue at appropriate place for sleep time
// No need to look before update head as those tasks have already timed out
// By working away from head we will chase the update head if it moves
BYTE i = GetSleepUpdateHead();
bit foundInsertionPoint = false;
while( !foundInsertionPoint )
{
 // Interrupt code for wakeup will be looking at the sleep queue
 // so we modify it while interrupts are disable
 // Interrupt routine does not modify sleep queue, it just looks at it
 // and adjusts it sleep update head pointer.
 SysCriticalSectionBegin();
 {
  if( i == SLEEP_QUEUE_HEAD )
  {
// start reached, so we insert the new task at this point
foundInsertionPoint = true;
  }
  else
  {
if( GetTaskStatus( i ) & TS_IS_WAKING ) // awaking ?
 ; // will move onto next task
else
{
// if not waking then we need to check remaining sleep time

 TICK_COUNT remainingSleep = GetTaskWakeUpTime( i ) - scheduler.os_tickCnt;
 // if this task has more time to sleep, ie will wake up later, than the new task
 // then we have found the insertion point in the list, ie in front of this task
 if( remainingSleep > sleepTime )
  foundInsertionPoint = true;
}
  }

  if( foundInsertionPoint )
  {	
{ // insert the new task before the current point

SetTaskWakeUpTime( hTask, scheduler.os_tickCnt + sleepTime );
 BYTE hPrev = GetPrevTask( i );

 // connect up new task into list
 SetNextTask( hTask, i );
 SetPrevTask( hTask, hPrev );

 // patch up existing task in list
 SetPrevTask( i, hTask );
 SetNextTask( hPrev, hTask );
}

// new task added before update head means that it will would never be checked as head
// is already passed this point so we must adjust it
if( i == GetSleepUpdateHead() )
 SetSleepUpdateHead( hTask );
SetTaskStatusBit( hTask, TS_IN_SLEEP_Q );  
SysCriticalSectionEnd();
  }
  else
i = GetNextTask( i ); // move on next task for examination
 }
 SysCriticalSectionEnd();
}
}

Share this post


Link to post
Share on other sites
neptun    0

Hi Dave,

 

thanks for the quick reply, your research concerning that issue and not to forget the kind support in even attach the rebuilt version of the library! :)

I'll try it out as soon as I can and will give my feedback here.

 

 

Cheers

neptun

Share this post


Link to post
Share on other sites
neptun    0

Hi Dave,

 

I wanna give you a first, quick feedback already.

Due to the fact that I was so curious now whether it'll work, I tried it instantly.

It definitely worked concerning the 2nd issue! :D

There is still an odd behaviour concerning the keypad but now I can get into that in more detail I guess. - So maybe I'll post here again.. ;-)

Thank you so much for the quick and positive support!!

By the way: which of the NOVO libraries need to get rebuild with that fixed issue?! By the way, I wonder why it didn't occur with the lib for the PIC18 cause I cannot discover any PIC16-specific binding in your code snippet...

 

Best regards

neptun

Share this post


Link to post
Share on other sites
JorgeF    0

Hi

 

Hi Jorge,

 

thanks for the "quick" analysis.

I didn't know (obviously) about the "automatical" GIE-interrupt-handling by the PIC hardware. Good to know that.

Although I don't think that this is the origin of the probblem cause I used this in other projects with BoostC in the same way and no more in the future... ;-)

Maybe you never had a problem by explicitly enabling interrupts (GIE=1) before the "retfie" instruction, but that is what I call "a disaster waiting to happen".

 

About the PIC inner workings on interrupt you can find this

When an interrupt is serviced:

• The GIE is cleared to disable any further interrupt.

• The return address is pushed onto the stack.

• The PC is loaded with 0004h.

and this

The Return from Interrupt instruction, RETFIE, exits

the interrupt routine, as well as sets the GIE bit, which

re-enables unmasked interrupts.

at section 14.3 page 208 of the PIC16F690 datasheet.

 

By looking at this code snipset generated by the BoostC compiler

...................
   	 }	// if ( pir1.RCIF == true ) // RX via UART occured?

	// (RE-)ENABLE ALL/GLOBAL INTERRUPT SOURCES!
	intcon.GIE = true;  // global interrupt enable
049A  178B	  BSF gbl_intcon,7


}
049B  0E56	  SWAPF Int1BContext+D'2', W
049C  0084	  MOVWF FSR
049D  0E55	  SWAPF Int1BContext+D'1', W
049E  008A	  MOVWF PCLATH
049F  0E54	  SWAPF Int1BContext, W
04A0  0083	  MOVWF STATUS
04A1  0EFF	  SWAPF Int1Context, F
04A2  0E7F	  SWAPF Int1Context, W
04A3  0009	  RETFIE

you can see that there are about 10 instruction betwen your code enabling interrupts (GIE=1) and the "retfie" instruction.

This piece of code is the "context restore" sequence.

If an interrupt is already pending or is trigered during this sequence, you will have have a reentrancy on the ISR and a context corruption.

The reentrancy main risk is stack overflow, a minor issue as it will need to happen a number of times.

But context corruption can wrek havoc in a program and have you staring for several days to a perfect algorithm trying to understand what is happening.

 

Has for your current problem, it looks like Dave already pinned it.

 

 

Best regards

Jorge

Edited by JorgeF

Share this post


Link to post
Share on other sites
neptun    0

Hi Jorge,

 

thanks for your detailed explanation that makes the possible disaster clear concerning my GIE-treatment.

You are definitely right that these are the problems that keeps one searching for the reason for long...!!

Those who are able to read (the relevant parts) have definitely an advantage. :rolleyes:

 

Best regards

neptun

Share this post


Link to post
Share on other sites
Dave    0

neptun,

...

By the way: which of the NOVO libraries need to get rebuild with that fixed issue?! By the way, I wonder why it didn't occur with the lib for the PIC18 cause I cannot discover any PIC16-specific binding in your code snippet...

All novo libraries need rebuilding.

The problem is there just waiting to happen when the right conditions are met.

 

Regards

Dave

Share this post


Link to post
Share on other sites

Your content will need to be approved by a moderator

Guest
You are commenting as a guest. If you have an account, please sign in.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoticons maximum are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×