Jump to content
Sign in to follow this  
Flyer

Very Impressed! - Example Novo Servo Driver

Recommended Posts

I've just spent a couple of days experimenting with Novo and am seriously impressed. This will simplify all my PIC projects.

 

For example find attached some code for a PIC 16F88 to drive a servo based on the position of a potentiometer (0-5VDC)

 

This uses 3 tasks:

The first reads the ADC and waits on a semaphore set in the interrupt routine to signal completion of conversion

The second cycles every 20msec to provide the basic timebase for the servo output, it uses the output of the ADC to set up timer1 as a oneshot which provides the variable length pulse, the pulse is turned off in the interrupt routine fired by the timer.

The third just pulses an output as a heartbeat.

 

At the same time the ADC routine outputs to an LCD display what its doing.

 

The Novo environment appears completely robust, in particular, I'm impressed that the LCD routines work within the tasks and that the embedded second timer interrupt works perfectly.

 

It did take some time to understand everything and get it all working - but a great investment in time for the future.

 

Key facts:

Don't forget the linker switches -swcs when you set up a new project using Novo

Do use version 6.7

 

David, Pavel; Many congrats on a great product

 

Best regards

 

Peter

 

 

///////////////////////////////////////////////
// Servo controller with LCD monitor
///////////////////////////////////////////////
// Uses Novo RTOS
//
// 
//
// Target Device: PIC16F88 20MHz
//
// Author: Peter Mather
//
// Version History:
// V1.0 - 28/5/2007

#include <system.h>
#include <novocfg_pic16t5e5ts1.h>
#include <novo.h>

#pragma CLOCK_FREQ 20000000
#pragma DATA _CONFIG, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_OFF & _WDT_OFF & _HS_OSC
//#pragma DATA _CONFIG, _CP_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_OFF & _WDT_OFF & _HS_OSC
#define LCD_ARGS 	2,	/* Interface type: mode 0 = 8bit, 1 = 4bit(low nibble), 2 = 4bit(upper nibble) */ \
	1, 				/* Use busy signal: 1 = use busy, 0 = use time delays */\
	PORTB, TRISB, 	/* Data port and data port tris register */ \
	PORTB, TRISB, 	/* Control port and control port tris register */ \
	0,				/* Bit number of control port is connected to RS */ \
	1,				/* Bit number of control port is connected to RW */ \
	3 				/* Bit number of control port is connected to Enable */
#include <lcd_driver.h> // include the LCD template code




#define hTask0 0
#define hTask1 1
#define hTask2 2
#define hSemaphore 0
static unsigned short conversion, times; 
void interrupt( void )
{
if( intcon.TMR0IF )
{
	// update system time every 1ms (actually is 204.8us x 5 = 1.024ms)
	static BYTE intDivider;
	if( ++intDivider == 5 )
	{
		intDivider = 0;
		SysTimerUpdate();
	}

	intcon.TMR0IF = 0; //clear TMR0 overflow flag
}

if(pir1.ADIF)
{
	pir1.ADIF = 0;
	SysSignalSemaphoreIsr(hSemaphore);

}

if (pir1.TMR1IF)
{
	t1con.TMR1ON =0; //turn off timer
	pir1.TMR1IF = 0;//reset flag
	porta.1 = 0;//turn off output
}
}

void InitTimer()
{
// configure Timer0	
option_reg.T0CS = 0; // use internal clock
option_reg.PSA = 0; // use prescaler form timer 0	


// so we get an interrupt around every 204.8us with 20MHz Clock
// set prescaller to divide by 4
option_reg.PS0 = 1;
option_reg.PS1 = 0;
option_reg.PS2 = 0;




// enable interrupts


intcon.PEIE = 1; 	//enable peripheral interrupts
pir1.ADIF = 0;		//clear ADC flag
pir1.TMR1IF=0; 		//clear timer 1 flag
t1con=0;			//set up timer1, system clock, no prescale, stopped
intcon.TMR0IE = 1; 	//enable TMR0 overflow bit	
pie1.TMR1IE=1; 		// enable timer 1 overflow interrupt enable
pie1.ADIE = 1; 		//enable ADC interrupt
intcon.GIE = 1; 	//enable global interrupts
}

void Task0()
{
while( 1 )
{


	HIBYTE(tmr1h,times);		//set timer duration
	LOBYTE(tmr1l,times);
	t1con.TMR1ON=1; //turn on timer
	porta.1 = 1;	//set output on
	Sys_Sleep( 20 );//sleep for 20msec

}
}

void Task1()
{
while(1)
{	
	clear_bit(pir1,ADIF);
	set_bit(adcon0,GO); //Start the conversion
	Sys_WaitSemaphore(hSemaphore, EVENT_NO_TIMEOUT);
	MAKESHORT(conversion,adresl,adresh);
	if(conversion<=12) 
		conversion=0; 
	else 
		conversion=conversion-12;
	if(conversion>=1000) conversion=999;
	conversion=conversion * 5;
	lcd_gotoxy(0,0);
	times=55536+conversion;
	lprintf("Reading %6d",conversion);
	lcd_gotoxy(0,1);
	lprintf("Writing %6u",times);
	Sys_Sleep( 100 );
}
}

void Task2()
{
while( 1 )
{
	porta.2 = 0;
	// 500ms delay
	Sys_Sleep( 250 ); // can only sleep for a maximum of 255 with 8 bit timers
	Sys_Sleep( 250 );
	porta.2 = 1;
	// 500ms delay
	Sys_Sleep( 250 );
	Sys_Sleep( 250 );
}
}

void main()
{	

ansel=1;
trisa = 0x01;
porta=0;
lcd_setup();
lprintf("test");
InitTimer();
SysInit();

SysCreateTask( hTask0, 2, Task0 );
SysCreateTask( hTask1, 4, Task1 );
SysCreateTask( hTask2, 4, Task2 );

SysStartTask( hTask0 );
SysStartTask( hTask1 );
SysStartTask( hTask2 );

adcon0=0;
set_bit(adcon1,ADCS2);
set_bit(adcon0,ADCS1);
clear_bit(adcon0,ADCS0); //set clock divide by 64;
set_bit(adcon1,ADFM); // Scale 0-1023
clear_bit(adcon0,CHS0);
clear_bit(adcon0,CHS1);
clear_bit(adcon0,CHS2); //Set channel to 0;
set_bit(adcon0,ADON); //turn on the A to D;	InitTimer();


while( 1 )
{	
	Sys_Yield();
}
}

Share this post


Link to post
Share on other sites
I've just spent a couple of days experimenting with Novo and am seriously impressed. This will simplify all my PIC projects.

 

For example find attached some code for a PIC 16F88 to drive a servo based on the position of a potentiometer (0-5VDC)

 

This uses 3 tasks:

The first reads the ADC and waits on a semaphore set in the interrupt routine to signal completion of conversion

The second cycles every 20msec to provide the basic timebase for the servo output, it uses the output of the ADC to set up timer1 as a oneshot which provides the variable length pulse, the pulse is turned off in the interrupt routine fired by the timer.

The third just pulses an output as a heartbeat.

 

At the same time the ADC routine outputs to an LCD display what its doing.

 

The Novo environment appears completely robust, in particular, I'm impressed that the LCD routines work within the tasks and that the embedded second timer interrupt works perfectly.

 

It did take some time to understand everything and get it all working - but a great investment in time for the future.

 

Key facts:

Don't forget the linker switches -swcs when you set up a new project using Novo

Do use version 6.7

 

David, Pavel; Many congrats on a great product

 

Best regards

 

Peter

 

 

///////////////////////////////////////////////

// Servo controller with LCD monitor

///////////////////////////////////////////////

// Uses Novo RTOS

//

//

//

// Target Device: PIC16F88 20MHz

//

// Author: Peter Mather

//

// Version History:

// V1.0 - 28/5/2007

 

#include <system.h>

#include <novocfg_pic16t5e5ts1.h>

#include <novo.h>

 

#pragma CLOCK_FREQ 20000000

#pragma DATA _CONFIG, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_OFF & _WDT_OFF & _HS_OSC

//#pragma DATA _CONFIG, _CP_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_OFF & _WDT_OFF & _HS_OSC

#define LCD_ARGS 2, /* Interface type: mode 0 = 8bit, 1 = 4bit(low nibble), 2 = 4bit(upper nibble) */ \

1, /* Use busy signal: 1 = use busy, 0 = use time delays */\

PORTB, TRISB, /* Data port and data port tris register */ \

PORTB, TRISB, /* Control port and control port tris register */ \

0, /* Bit number of control port is connected to RS */ \

1, /* Bit number of control port is connected to RW */ \

3 /* Bit number of control port is connected to Enable */

#include <lcd_driver.h> // include the LCD template code

 

 

 

 

#define hTask0 0

#define hTask1 1

#define hTask2 2

#define hSemaphore 0

static unsigned short conversion, times;

void interrupt( void )

{

if( intcon.TMR0IF )

{

// update system time every 1ms (actually is 204.8us x 5 = 1.024ms)

static BYTE intDivider;

if( ++intDivider == 5 )

{

intDivider = 0;

SysTimerUpdate();

}

 

intcon.TMR0IF = 0; //clear TMR0 overflow flag

}

 

if(pir1.ADIF)

{

pir1.ADIF = 0;

SysSignalSemaphoreIsr(hSemaphore);

 

}

 

if (pir1.TMR1IF)

{

t1con.TMR1ON =0; //turn off timer

pir1.TMR1IF = 0;//reset flag

porta.1 = 0;//turn off output

}

}

 

void InitTimer()

{

// configure Timer0

option_reg.T0CS = 0; // use internal clock

option_reg.PSA = 0; // use prescaler form timer 0

 

 

// so we get an interrupt around every 204.8us with 20MHz Clock

// set prescaller to divide by 4

option_reg.PS0 = 1;

option_reg.PS1 = 0;

option_reg.PS2 = 0;

 

 

 

 

// enable interrupts

 

 

intcon.PEIE = 1; //enable peripheral interrupts

pir1.ADIF = 0; //clear ADC flag

pir1.TMR1IF=0; //clear timer 1 flag

t1con=0; //set up timer1, system clock, no prescale, stopped

intcon.TMR0IE = 1; //enable TMR0 overflow bit

pie1.TMR1IE=1; // enable timer 1 overflow interrupt enable

pie1.ADIE = 1; //enable ADC interrupt

intcon.GIE = 1; //enable global interrupts

}

 

void Task0()

{

while( 1 )

{

 

 

HIBYTE(tmr1h,times); //set timer duration

LOBYTE(tmr1l,times);

t1con.TMR1ON=1; //turn on timer

porta.1 = 1; //set output on

Sys_Sleep( 20 );//sleep for 20msec

 

}

}

 

void Task1()

{

while(1)

{

clear_bit(pir1,ADIF);

set_bit(adcon0,GO); //Start the conversion

Sys_WaitSemaphore(hSemaphore, EVENT_NO_TIMEOUT);

MAKESHORT(conversion,adresl,adresh);

if(conversion<=12)

conversion=0;

else

conversion=conversion-12;

if(conversion>=1000) conversion=999;

conversion=conversion * 5;

lcd_gotoxy(0,0);

times=55536+conversion;

lprintf("Reading %6d",conversion);

lcd_gotoxy(0,1);

lprintf("Writing %6u",times);

Sys_Sleep( 100 );

}

}

 

void Task2()

{

while( 1 )

{

porta.2 = 0;

// 500ms delay

Sys_Sleep( 250 ); // can only sleep for a maximum of 255 with 8 bit timers

Sys_Sleep( 250 );

porta.2 = 1;

// 500ms delay

Sys_Sleep( 250 );

Sys_Sleep( 250 );

}

}

 

void main()

{

 

ansel=1;

trisa = 0x01;

porta=0;

lcd_setup();

lprintf("test");

InitTimer();

SysInit();

 

SysCreateTask( hTask0, 2, Task0 );

SysCreateTask( hTask1, 4, Task1 );

SysCreateTask( hTask2, 4, Task2 );

 

SysStartTask( hTask0 );

SysStartTask( hTask1 );

SysStartTask( hTask2 );

 

adcon0=0;

set_bit(adcon1,ADCS2);

set_bit(adcon0,ADCS1);

clear_bit(adcon0,ADCS0); //set clock divide by 64;

set_bit(adcon1,ADFM); // Scale 0-1023

clear_bit(adcon0,CHS0);

clear_bit(adcon0,CHS1);

clear_bit(adcon0,CHS2); //Set channel to 0;

set_bit(adcon0,ADON); //turn on the A to D; InitTimer();

 

 

while( 1 )

{

Sys_Yield();

}

}

 

Hi! Flyer

I'm very interested to probe your code but I can't simulate it in boostc++, I lead the Signal Generator plugin with the checkbox for DC signal in the porta.0 and nothing change, then, I comment the line Sys_WaitSemaphore(hSemaphore, EVENT_NO_TIMEOUT); to avoid that the task1 goes to break, but nothing work, only changes the hartbeat at task0 and RA1 in the task2.

can you simulate with the boostc++ for full debuging your code?

thanks

Share this post


Link to post
Share on other sites

Hi! Flyer

In accordance with the configuration file name novocfg_pic16t5e5ts1.h you re-build it the Novo libraries for 5 tasks, 5 events and the timer size with a one byte, and no priority because the letter P was ommited at the end of file name. I don't see this amount of events in the code and have only 3 tasks, why do you re-compile with this extras tasks and events? is this tasks hidden for us, or you just make this to probe how to re-build libraries? is this file name equal to the configurations within the file? is not missing the function SysSignalSemaphore(hSemaphore) at the start of program?

thanks

Edited by Doglao

Share this post


Link to post
Share on other sites

Hi!

I changed the target device to pic16F877A with few changes in the A/D configurations to simulate with SourceBoost IDE using variable PSU to the port A0, this works fine now and I can see how this code works, is very pretty how this tasks running, and how is used the Novo API to handle events, very good example, I understand more about Novo RTOS with this example.

thanks

Douglas :P

Share this post


Link to post
Share on other sites

Douglas

 

Sorry I haven't replied to your other posts - I've been working on something else recently and not looking at this board. Glad you've got it all working and the code was useful. The library rebuild was just a generic to cover my likely future uses.

 

Best Regards

 

Peter

 

Hi!

I changed the target device to pic16F877A with few changes in the A/D configurations to simulate with SourceBoost IDE using variable PSU to the port A0, this works fine now and I can see how this code works, is very pretty how this tasks running, and how is used the Novo API to handle events, very good example, I understand more about Novo RTOS with this example.

thanks

Douglas B)

Share this post


Link to post
Share on other sites

Peter thanks so much for this posting. I've been out of the business for 12 years now and I was really struggling trying to get a handle on PIC programming. I ported your application to a PIC18F4520 and it forced me to study the interrupts, timers, ADC usage and of course NOVORtos. I have it all running, still playing with timers a little trying to get accurate clocks.

Pavel and David -- Great Job!!!

Thanks again,

mark

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji 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...
Sign in to follow this  

×
×
  • Create New...