Flyer 0 Posted May 28, 2007 Report Share Posted May 28, 2007 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(); } } Quote Link to post Share on other sites
Doglao 0 Posted December 13, 2007 Report Share Posted December 13, 2007 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 Quote Link to post Share on other sites
Doglao 0 Posted December 14, 2007 Report Share Posted December 14, 2007 (edited) 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 December 15, 2007 by Doglao Quote Link to post Share on other sites
Doglao 0 Posted December 18, 2007 Report Share Posted December 18, 2007 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 Quote Link to post Share on other sites
Flyer 0 Posted January 3, 2008 Author Report Share Posted January 3, 2008 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 Quote Link to post Share on other sites
MarkS 0 Posted January 3, 2008 Report Share Posted January 3, 2008 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 Quote Link to post Share on other sites
Recommended Posts
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.