In previous post I putted some examples of using the dsPIC30F2010 prototype board. However it's too long. So I need to write some remaining posts here.
 |
PCBWay.com Sponsor PCB Project |
Creating a PWM Output Using Code Generation Wizard
Generating a PWM output signal could be done from scratch with a few line of code using CCS PICC. We can use its code generation wizard or even manually.
 |
PWM Output Pin of OC1 |
After clicking on Create Project it will generate source code. Then pressing F9 to compile this project.
The main.c C source code is just like below.
- #include <main.h>
- void main()
- {
- while(TRUE)
- {
- //TODO: User Code
- }
- }
Then double click on the main.h header file we will see its source code.
- #include <30F2010.h>
- #device ICSP=1
- #use delay(crystal=20000000)
- #FUSES NOWDT //No Watch Dog Timer
- #FUSES CKSFSM //Clock Switching is enabled, fail Safe clock monitor is enabled
- #use pwm(OC1,TIMER=2,FREQUENCY=10000,DUTY=0)
It will generate a PWM signal output at pin OC1 (RC13) with a frequency of 10kHz and 0% duty cycle. If we want a 50% duty cycle we need to change the DUTY parameter to 50, and rebuilt it.
PWM Duty Cycle Adjusting with ADC
After using the code generation wizard I got some idea of using PWM in CCS PICC. So I modify and write more codes to adjust PWM signal. I use the on-board ADC input from a potentiometer. Then it will convert to PWM duty cycle ranging from 0% to 100%.
- #include "board.h"
- #use pwm(OC1,TIMER=2,FREQUENCY=10000,STREAM=_1,DUTY=50)
- void main()
- {
- long adc_value = 0;
- float duty_cycle = 0;
- setup_adc_ports(sAN4);
- setup_adc(ADC_CLOCK_INTERNAL | ADC_TAD_MUL_31);
- while(TRUE)
- {
- //TODO: User Code
- set_adc_channel(4);
- delay_us(10);
- adc_value = read_adc();
- duty_cycle = (1000.0*adc_value)/1023;
- pwm_set_duty(_1,(int)duty_cycle);
- //pwm_set_duty_percent(_1,(int)duty_cycle);
- delay_ms(100);
- }
- }
And its "board.h" file:
- #include <30F2010.h>
- #device ADC=10
- #device ICSP=1
- #fuses HS,NODEBUG,NOWDT,PR,CKSFSM
- #use delay(crystal=20000000)
- #use FIXED_IO( D_outputs=PIN_D1,PIN_D0 )
- #use rs232(UART1, baud=9600, stream=UART_PORT1)
- #define LED0 PIN_D0
- #define LED1 PIN_D1
- #define SW0 PIN_C13
- #define SW1 PIN_C14
- #define DELAY 500
It work fine without wiring additional components. However we can connect the OC1(RC13) PWM pin to a larger LED ( for instance a 5VDC 10mm LED).
 |
Low Duty Cycle |
 |
High Duty Cycle |
Click here to download its source file.
ADC with On-board Tactile Switches and PWM
Fortunately there are two on-board tactile switches that could be used to adjust PWM duty cycle. So I will use ADC channel 4, SW4 (RC13) and SW5(RC14) to adjust PWM duty cycle of OC1 and OC2 respectively.
 |
PWM Adjustment Using Pot and Tactile Switches |
The PWM OC1 is generated by Timer 2 while the PWM OC2 is generated by Timer 3.
- #include "board.h"
- #use pwm(OC1,TIMER=2,FREQUENCY=10000,STREAM=_1,DUTY=0)
- #use pwm(OC2,TIMER=3,FREQUENCY=10000,STREAM=_2,DUTY=0)
- void main()
- {
- long adc_value = 0;
- int oc2_count=0;
- float duty_cycle = 0;
- set_pullup(TRUE,PIN_C13);
- set_pullup(TRUE,PIN_C14);
- setup_adc_ports(sAN4);
- setup_adc(ADC_CLOCK_INTERNAL | ADC_TAD_MUL_31);
- while(TRUE)
- {
- //TODO: User Code
- set_adc_channel(4);
- delay_us(10);
- adc_value = read_adc();
- duty_cycle = (1000.0*adc_value)/1023;
- pwm_set_duty(_1,(int)duty_cycle);
-
- if(input(SW0)==0){
- if(oc2_count<1000) oc2_count+=100;
- pwm_set_duty(_2,oc2_count);
- delay_ms(250);
- }
- if(input(SW1)==0){
- if(oc2_count>0) oc2_count-=100;
- pwm_set_duty(_2,oc2_count);
- delay_ms(250);
- }
- }
- }
Its "board.h" header file:
- #include <30F2010.h>
- #device ADC=10
- #device ICSP=1
- #fuses HS,NODEBUG,NOWDT,PR,CKSFSM
- #use delay(crystal=20000000)
- #use rs232(UART1, baud=9600, stream=UART_PORT1)
- #define LED0 PIN_D0
- #define LED1 PIN_D1
- #define SW0 PIN_C13
- #define SW1 PIN_C14
- #define DELAY 500
Click here to download its source file.
Timer Tick Example
We can use timer tick for timing delay and scheduling instead of using the delay function. We can call it from scratch using the CCS PICC IDE PIC24 project wizard.
 |
Timer Tick Example Using Timer 1 |
Then we get generated code lists below.- #include <30F2010.h>
- #device ICSP=1
- #use delay(crystal=20000000)
- #FUSES NOWDT //No Watch Dog Timer
- #FUSES CKSFSM //Clock Switching is enabled, fail Safe clock monitor is enabled
- #use timer(timer=1,tick=100us,bits=32,NOISR)
- #define TICK_TYPE unsigned int32
- #include <main.h>
- TICK_TYPE GetTickDifference(TICK_TYPE currTick, TICK_TYPE prevTick)
- {
- return(currTick-prevTick);
- }
- void timer_1_tick(void)
- {
- //TODO: User Code
- }
- void main()
- {
- TICK_TYPE CurrentTick,PreviousTick;
- //Example program using Tick Timer
- CurrentTick = PreviousTick = get_ticks();
- while(TRUE)
- {
- CurrentTick = get_ticks();
- if(GetTickDifference(CurrentTick, PreviousTick) >= ((TICK_TYPE)TICKS_PER_SECOND*1)/1000)
- {
- timer_1_tick();
- PreviousTick = CurrentTick;
- }
- //TODO: User Code
- }
- }
Then I need to add some codes to these existing source codes.
- #include <30F2010.h>
- #device ICSP=1
- #use delay(crystal=20000000)
- #FUSES NOWDT //No Watch Dog Timer
- #FUSES CKSFSM //Clock Switching is enabled, fail Safe clock monitor is enabled
- #use FIXED_IO( D_outputs=PIN_D1,PIN_D0 )
- #define LED1 PIN_D0
- #define LED2 PIN_D1
- #use timer(timer=1,tick=100us,bits=32,NOISR)
- #define TICK_TYPE unsigned int32
- #include <main.h>
- unsigned int16 count_1_ms=0, count_100_ms=0;
- TICK_TYPE GetTickDifference(TICK_TYPE currTick, TICK_TYPE prevTick)
- {
- return(currTick-prevTick);
- }
- void timer_1_tick(void)
- {
- //TODO: User Code
- count_1_ms++;
- if(count_1_ms>=100) {
- output_toggle(LED2);
- count_1_ms=0;
- count_100_ms++;
- }
- if(count_100_ms>=5){
- output_toggle(LED1);
- count_100_ms=0;
- }
- }
- void main()
- {
- TICK_TYPE CurrentTick,PreviousTick;
- //Example program using Tick Timer
- CurrentTick = PreviousTick = get_ticks();
- while(TRUE)
- {
- CurrentTick = get_ticks();
- if(GetTickDifference(CurrentTick, PreviousTick) >= ((TICK_TYPE)TICKS_PER_SECOND*1)/1000)
- {
- timer_1_tick();
- PreviousTick = CurrentTick;
- }
- //TODO: User Code
- }
- }
It will blink LEDs connect to RD0 and RD1 at different rates.
 |
RD0 and RD1 at different rates |
 |
RD0 and RD1 at different rates |
Click here to download its source file.
Timer 1 Interrupt Example Using Coder Generation Wizard
Timer1 module operates in many modes up to software configuration, internal, gated and external. We can write its source manually or even using the CCS PICC code generation wizard.
 |
Using Code Generation Wizard |
Then it will generate a skeleton code below that we have to add more code manually.
- #include <main.h>
- #INT_TIMER1
- void timer1_isr(void)
- {
- }
- void main()
- {
- setup_timer1(TMR_INTERNAL | TMR_DIV_BY_1, 1000);
- enable_interrupts(INT_TIMER1);
- enable_interrupts(INTR_GLOBAL);
- while(TRUE)
- {
- //TODO: User Code
- }
- }
Then I need to add more codes both in main function and Timer interrupt service routine "#INT_TIMER1".
- #include <main.h>
- unsigned int16 timer_1_counts=0;
- #INT_TIMER1
- void timer1_isr(void)
- {
- output_toggle(PIN_D0);
- timer_1_counts++;
- clear_interrupt(INT_TIMER1);
- }
- void main()
- {
- setup_timer1(TMR_INTERNAL | TMR_DIV_BY_256, 1000);
- enable_interrupts(INT_TIMER1);
- enable_interrupts(INTR_GLOBAL);
- while(TRUE)
- {
- //TODO: User Code
- if(timer_1_counts>=100){
- output_toggle(pin_d1);
- timer_1_counts=0;
- }
- }
- }
This source codes will blink pin RD0 and RD1 at different rates. Click here to download its source file.Change Notify (CN) Interrupt
Change Notify Interrupt (CNI) allow the program to fast response to external event change. For instance a change from logic high to low or vice versa. There are two tactile switches connect to RC13(CN1) and RC14(CN0) respectively. There are no external pull up or pull down resistors. So you need to add more external components to detect input logic change requirement.
 |
TABLE 8-2: INPUT CHANGE NOTIFICATION REGISTER MAP (BITS 15-0) |
Fortunately the Change Notify Interrupt (CNI) come with internal pull-up enable feature that we can enable or disable it by software setting. The CNENx and CNPUx special register responsible for this task.
The program below demonstrate a simple use of Change Notify (CN) Interrupt of pin CN0(RC14). The LED connects to pin RD0 keeps blinking at the rate of 500ms in main program's loop. Whenever the CN0 input logic changes to logic low the CNI occurs. It will toggle the LED connects to pin RD1.
- #include <main.h>
- /*CN Pull Up Enable Register*/
- #byte CNPU1=0x0C4;
- #bit CN0PU=CNPU1.0;
- /*CN Interrupt Enable Register*/
- #byte CNIEN1=0x0C0;
- #bit CN0EN=CNIEN1.0;
- /*Interrupt Service Routine for CN Interrupt*/
- #INT_CNI
- void cni_isr(void)
- {
- if(!input(CN0)) output_toggle(LED2);
- clear_interrupt(INT_CNI);
- }
- void main()
- {
- /*PortC As Inputs*/
- set_tris_c(0xFFFF);
- /*Turn On CN0 PullUp and CN0 Interrupt*/
- CN0PU=1;
- CN0EN=1;
- /*Enable Interrupt*/
- enable_interrupts(INTR_CN_PIN|PIN_C13);
- enable_interrupts(INTR_GLOBAL);
- clear_interrupt(INT_CNI);
- while(TRUE)
- {
- //TODO: User Code
- output_toggle(LED1);
- delay_ms(500);
- }
- }
- #include <30F2010.h>
- #device ICSP=1
- #use delay(crystal=20000000)
- #FUSES NOWDT
- //No Watch Dog Timer
- #FUSES CKSFSM
- //Clock Switching is enabled, fail Safe clock monitor is enabled
- #use FIXED_IO( D_outputs=PIN_D1,PIN_D0 )
- #define CN1 PIN_C13
- #define CN0 PIN_C14
- #define LED1 PIN_D0
- #define LED2 PIN_D1
I tested this demo program on my dsPIC30F2010 prototype board. It work fine and very fast. Click here to download this example program.
Now I use all tactile switches connect to RC13 and RC14 to generate Change Notification Interrupt (CNI). Every time the CN interrupts occur it will toggle the LEDs.
- #include <main.h>
- /*CN Pull Up Enable Register*/
- #byte CNPU1=0x0C4;
- #bit CN0PU=CNPU1.0;
- #bit CN1PU=CNPU1.1;
- /*CN Interrupt Enable Register*/
- #byte CNIEN1=0x0C0;
- #bit CN0EN=CNIEN1.0;
- #bit CN1EN=CNIEN1.1;
- /*Interrupt Service Routine for CN Interrupt*/
- #INT_CNI
- void cni_isr(void)
- {
- if(!input(CN0)) output_toggle(LED2);
- if(!input(CN1)) output_toggle(LED1);
- clear_interrupt(INT_CNI);
- }
- void main()
- {
- /*PortC As Inputs*/
- set_tris_c(0xFFFF);
- /*Turn On CN0 PullUp and CN0 and CN1 Interrupt*/
- CN0PU=1;
- CN0EN=1;
- CN1PU=1;
- CN1EN=1;
- /*Enable Interrupt*/
- //enable_interrupts(INTR_CN_PIN|PIN_C13);
- //enable_interrupts(INTR_CN_PIN|PIN_C14);
- enable_interrupts(INT_CNI);
- enable_interrupts(INTR_GLOBAL);
- clear_interrupt(INT_CNI);
- while(TRUE)
- {
- //TODO: User Code
- }
- }
I take some special registers of CN interrupt to use in this program because using the CCS PICC built-in library is not enough.
- #include <30F2010.h>
- #device ICSP=1
- #use delay(crystal=20000000)
- #FUSES NOWDT
- //No Watch Dog Timer
- #FUSES CKSFSM
- //Clock Switching is enabled, fail Safe clock monitor is enabled
- #use FIXED_IO( D_outputs=PIN_D1,PIN_D0 )
- #define CN1 PIN_C13
- #define CN0 PIN_C14
- #define LED1 PIN_D0
- #define LED2 PIN_D1
There are some bounce while pressing the tactile switches. So it's suitable to add and RC filter circuit to eliminate this noise. Using a software delay in the ISR is a good choice to bypass this noisy bouncing period.
Click here to download this program example.