The example shows a method of formation of a menu with options on an LCD in 4-bit mode. Setting of bit 1 on port F means go to the next option, whereas setting of bit 0 on port F means go to the previous option. The rate of transition to the next/previous option is limited so the maximum of 4 transitions per second is allowed.
/* This project is designed to work with PIC P30F6014A. It has been tested on dsPICPRO3 development system with 10.0 MHz crystal and 8xPLL. It should work with any other crystal. Note: the maximum operating frequency for dsPIC is 120MHz. With minor adjustments, this example should work with any other dsPIC MCU */ signed short menu_index; char menu[5][7] = {"First" , // Menu items "Second", "Third" , "Fourth", "Fifth" } absolute 0x1880; // Directive absolute specifies the starting address // in RAM for a variable. void main() { ADPCFG = 0xFFFF; // Configure PORTB as digital TRISF = 0xFFFF; // Configure PORTF as input (menu control) menu_index = 0; // Init menu_item[0] // Init LCD in 4-bit mode for dsPICPRO3 board Lcd_Custom_Config(&PORTD, 7,6,5,4, &PORTB, 4,0,6); Lcd_Custom_Cmd(LCD_CURSOR_OFF); Lcd_Custom_Cmd(LCD_FIRST_ROW); Lcd_Custom_Out(1,1,"Menu :"); Lcd_Custom_Out(1,8,menu[menu_index]); // Show menu element on LCD while (1) { // endless loop if (PORTF.F1 == 1) { // Detect logical one on RF1 pin => MENU UP menu_index = menu_index + 1; // Next index in menu if (menu_index > 4) menu_index = 0; // Circular menu Lcd_Custom_Out(1,8, " "); // Clear text Lcd_Custom_Out(1,8,menu[menu_index]); // Show menu element on LCD Delay_ms(250); // No more than 4 changes per sec } if (PORTF.F0 == 1) { // Detect logical one on RF0 pin => MENU DOWN menu_index = menu_index - 1; // Previous index in menu if (menu_index < 0) menu_index = 4; // Circular menu Lcd_Custom_Out(1,8, " "); // Clear text Lcd_Custom_Out(1,8,menu[menu_index]); // Show menu element on LCD Delay_ms(250); // No more than 4 changes per sec } } }
/* This project is designed to work with PIC P30F6014A. It has been tested on dsPICPRO3 board with 10.0 MHz crystal and 8xPLL. It should work with any other crystal. Note: the maximum operating frequency for dsPIC is 120MHz. With minor adjustments, this example should work with any other dsPIC MCU On-board DAC module Enable SPI connection to DAC on SW4 and DAC's Load(LD) and Chip Select(CS) pins on SW3. */ #include // to be used with spi_init_advanced routine // *** Filter Designer Tool outputs *** // const unsigned int BUFFER_SIZE = 8; const unsigned int FILTER_ORDER = 3; const signed int COEFF_B[FILTER_ORDER+1] = {0x21F3, 0x65DA, 0x65DA, 0x21F3}; const signed int COEFF_A[FILTER_ORDER+1] = {0x2000, 0xB06D, 0x47EC, 0xE8B6}; const unsigned int SCALE_B = 6; const unsigned int SCALE_A = -2; // *** DAC pinout *** // const char CS_PIN = 1; // DAC CS pin const char LOAD_PIN = 2; // DAC LOAD pin unsigned int THalf_Low, THalf_High; // half-periods of low and high-frequency // square signals char char2send; // char recived from UART unsigned int sample, sending_ch_cnt; // digital signal sample, sending char counter unsigned int us_cntL, us_cntH; // low and high-frequency square signal // microseconds counters signed int input[BUFFER_SIZE]; // filter input signal (two square signals) signed int output[BUFFER_SIZE]; // filtered signal unsigned int sample_index; // index of current sample signed int voltageL, voltageH; // square signals amplitudes void InitMain() { LATC.CS_PIN = 1; // set DAC CS to inactive LATC.LOAD_PIN = 0; // set DAC LOAD to inactive TRISC.LOAD_PIN = 0; // configure DAC LOAD pin as output TRISC.CS_PIN = 0; // configure DAC CS pin as output // Initialize SPI2 module Spi2_Init_Advanced(_SPI_MASTER, _SPI_16_BIT, _SPI_PRESCALE_SEC_1, _SPI_PRESCALE_PRI_1, _SPI_SS_DISABLE, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_HIGH, _SPI_ACTIVE_2_IDLE); Uart1_Init(9600); // Initialize UART1 module } void DAC_Output(unsigned int valueDAC) { LATC.CS_PIN = 0; // CS enable for DAC // filter output range is 16-bit number; DAC input range is 12-bit number valueDAC = valueDAC >> 4; // now both numbers are 12-bit, but filter output is signed and DAC input is unsigned. // Half of DAC range 4096/2=2048 is added to correct this valueDAC = valueDAC + 2048; SPI2BUF = 0x3000 | valueDAC; // write valueDAC to DAC (0x3 is required by DAC) while (SPI2STAT.F1) // wait for SPI module to finish sending asm nop; LATC.CS_PIN = 1; // CS disable for DAC } void SetPeriods(char ch) { /* DTMF frequencies: 1209 Hz 1336 Hz 1477 Hz 1633 Hz 697 Hz 1 2 3 A 770 Hz 4 5 6 B 852 Hz 7 8 9 C 941 Hz * 0 # D */ // Calculate half-periods in microseconds // example: 1/697Hz = 0.001435 seconds = 1435 microseconds // 1435/2 = 717 switch (ch) { case 49: THalf_Low = 717; THalf_High = 414; break; //'1' case 50: THalf_Low = 717; THalf_High = 374; break; //'2' case 51: THalf_Low = 717; THalf_High = 339; break; //'3' case 65: THalf_Low = 717; THalf_High = 306; break; //'A' case 52: THalf_Low = 649; THalf_High = 414; break; //'4' case 53: THalf_Low = 649; THalf_High = 374; break; //'5' case 54: THalf_Low = 649; THalf_High = 339; break; //'6' case 66: THalf_Low = 649; THalf_High = 306; break; //'B' case 55: THalf_Low = 587; THalf_High = 414; break; //'7' case 56: THalf_Low = 587; THalf_High = 374; break; //'8' case 57: THalf_Low = 587; THalf_High = 339; break; //'9' case 67: THalf_Low = 587; THalf_High = 306; break; //'C' case 42: THalf_Low = 531; THalf_High = 414; break; //'*' case 48: THalf_Low = 531; THalf_High = 374; break; //'0' case 35: THalf_Low = 531; THalf_High = 339; break; //'#' case 68: THalf_Low = 531; THalf_High = 306; break; //'D' } } void ClearBufs() { //Clear buffers Vector_Set(input, BUFFER_SIZE, 0); Vector_Set(output, BUFFER_SIZE, 0); } void Timer1Int() org 0x1A { // interrupt frequency is 20kHz // calculate sample sample = voltageL + voltageH; // add voltages input[sample_index] = sample; // write sample to input buffer // update low-frequency square signal microseconds counter us_cntL = us_cntL + 50; // since us_cntL and THalf_Low are in microseconds // and Timer1 interrupt occures every 50us // increment us_cntL by 50 if (us_cntL > THalf_Low) { // half-period exceeded, change sign voltageL = -voltageL; us_cntL = us_cntL - THalf_Low; // subtract half-period } // update high-frequency square signal microseconds counter us_cntH = us_cntH + 50; if (us_cntH > THalf_High) { voltageH = -voltageH; us_cntH = us_cntH - THalf_High; } //IIR(amp), filtering new sample sample = IIR_Radix(SCALE_B, SCALE_A, COEFF_B, COEFF_A, FILTER_ORDER+1, input, BUFFER_SIZE, output, sample_index); DAC_Output(sample); // send sample to digital-to-analog converter output[sample_index] = sample; // write filtered sample in output buffer sample_index++; // increment sample index, prepare for next sample if (sample_index >= BUFFER_SIZE) sample_index = 0; sending_ch_cnt--; // decrement char sending counter // (character transmition lasts 90ms = 1800 samples) if (sending_ch_cnt == 0) { // if character transmition is over T1CON=0; // turn off Timer1 Delay_ms(200); // pause between two characters is 200ms } IFS0.F3 = 0; // clear Timer1 interrupt flag } // --- main --- // void main() { InitMain(); // perform initializations sending_ch_cnt = 0; // reset counter sample_index = 0; // initialize sample index // Clear interrupt flags IFS0 = 0; IFS1 = 0; IFS2 = 0; INTCON1 = 0x8000; // disable nested interrupts IEC0 = 0x0008; // enable Timer1 interrupt // Timer1 input clock is Fosc/4. Sampling frequency is 20kHz. Timer should // raise interrupt every 50 microseconds. PR1 = (Fosc[Hz]/4) / 20000Hz = Fosc[kHz]/(4*20) PR1 = Clock_kHz() / 80; // Note: interrupt routine execution takes ~10us while (1) { // endless loop if ((sending_ch_cnt == 0) && // check if sending of previous character is over (Uart1_Data_Ready() > 0)) { // check if character arrived via UART1 char2send = Uart1_Read_Char(); // read data from UART and store it SetPeriods(char2send); // set periods for low and high-frequency square signals ClearBufs(); // clear input and output buffers // digital filter computing error is smaller for signals of higher amplitudes // so signal amplitude should as high as possible. The highest value for // signed integer type is 0x7FFF but since we are adding 2 signals we must // divide it by 2. voltageH = 0x7FFF / 2; // high-frequency square signal amplitude voltageL = 0x7FFF / 2; // low-frequency square signal amplitude us_cntL = 0; // low-frequency square signal microseconds counter us_cntH = 0; // high-frequency square signal microseconds counter // start Timer T1 sending_ch_cnt = 1800; // character tansmition lasts 90ms = 1800 samples * 50us T1CON = 0x8000; // enable Timer1 (TimerOn, prescaler 1:1) } } }
/* This project is designed to work with PIC P30F6014A. It has been tested on dsPICPRO3 board with 10.0 MHz crystal and 8xPLL. It should work with any other crystal. Note: the maximum operating frequency for dsPIC is 120MHz. With minor adjustments, this example should work with any other dsPIC MCU */ #include <Spi_Const.h> // *** DAC pinout *** // const LOAD_PIN = 2; // DAC load pin const CS_PIN = 1; // DAC CS pin // filter setup: // filter kind: IIR // filter type: lowpass filter // filter order: 4 // design method: Chebyshev type II const unsigned int BUFFER_SIZE = 8; const unsigned int FILTER_ORDER = 4; const unsigned int BPF1_COEFF_B[FILTER_ORDER+1] = {0x1BD7, 0xAB5D, 0x753A, 0xAB5D, 0x1BD7}; const unsigned int BPF1_COEFF_A[FILTER_ORDER+1] = {0x2000, 0xA1C7, 0x6C59, 0xC6EA, 0x0BDE}; const unsigned int BPF1_SCALE_B = 0; const unsigned int BPF1_SCALE_A = -2; // filter setup: // filter kind: IIR // filter type: Highpass filter // filter order: 4 // design method: Chebyshev type II const unsigned int BPF2_COEFF_B[FILTER_ORDER+1] = {0x0BF7, 0xD133, 0x45AF, 0xD133, 0x0BF7}; const unsigned int BPF2_COEFF_A[FILTER_ORDER+1] = {0x1000, 0xCA8B, 0x44B5, 0xD7E5, 0x08F3}; const unsigned int BPF2_SCALE_B = -3; const unsigned int BPF2_SCALE_A = -3; // min voltage offset level on ADC that can be detected as DTMF const unsigned int MinLevel = 18; char SignalActive; // indicator (if input signal exists) int sample; // temp variable used for reading from ADC char Key; // detected character long int f; // detected frequency unsigned SampleCounter; // indicates the number of samples in circular buffer unsigned sample_index; // index of next sample int input[8]; // circular buffer - raw samples (directly after ADC) int output_f1[8]; // circular buffer - samples after IIR BP filter int output_f2[8]; // circular buffer - samples after IIR BP filter unsigned TransitLow, TransitHigh; // counts of transitions (low, high freq) int sgnLow, sgnHigh; // current signs of low and high freq signal int KeyCnt; // number of recived DTFM and displayed on LCD void Estimate(){ unsigned fd; /* DTMF frequencies: 1209 Hz 1336 Hz 1477 Hz 1633 Hz 697 Hz 1 2 3 A 770 Hz 4 5 6 B 852 Hz 7 8 9 C 941 Hz * 0 # D */ // calculating index of lower freq f = TransitLow*20000l; // f = No_Of_Transitions*Sampling_Freq [Hz] f = f >> 11; // f = f div 2048 = f/2/1024 (2 transitions in each period) if (f < 733) fd = 1; //Index of Low_freq = 1 else if (f < 811) fd = 2; //Index of Low_freq = 2 else if (f < 896) fd = 3; //Index of Low_freq = 3 else fd = 4; //Index of Low_freq = 4 // calculating index of higher freq f = TransitHigh*20000l; // f = No_Of_Transitions*Sampling_Freq f = f >> 11; // f = f/2048 = f/2/1024 (2 transitions in each period) if (f<1272) fd = fd + 10; // encode Index of higher freq as 10 else if (f<1406) fd = fd + 20; // encode Index of higher freq as 20 else if (f<1555) fd = fd + 30; // encode Index of higher freq as 30 else fd = fd + 40; // encode Index of higher freq as 40 switch (fd){ // reading of input char from DTMF matrix case 11: Key = '1'; break; case 12: Key = '4'; break; case 13: Key = '7'; break; case 14: Key = '*'; break; case 21: Key = '2'; break; case 22: Key = '5'; break; case 23: Key = '8'; break; case 24: Key = '0'; break; case 31: Key = '3'; break; case 32: Key = '6'; break; case 33: Key = '9'; break; case 34: Key = '#'; break; case 41: Key = 'A'; break; case 42: Key = 'B'; break; case 43: Key = 'C'; break; case 44: Key = 'D'; break; } // diplay recived char on second row of LCD if(KeyCnt >= 16) { // if second row is full erase it and postion cursor at first column Lcd_Custom_Cmd(LCD_SECOND_ROW); Lcd_Custom_Out_CP(" "); Lcd_Custom_Cmd(LCD_SECOND_ROW); KeyCnt = 0; // reset recived DTFM signals counter } Lcd_Custom_Chr_CP(Key); // output recived on LCD KeyCnt++; // increment counter } void DAC_Output(unsigned valueDAC){ LATC.CS_PIN = 0; // CS enable for DAC // filter output range is 16-bit number; DAC input range is 12-bit number valueDAC = valueDAC >> 4; // now both numbers are 12-bit but filter output is signed and DAC input is unsigned. // half of DAC range 4096/2=2048 is added to correct this valueDAC = valueDAC + 2048; SPI2BUF = 0x3000 | valueDAC; // write valueDAC to DAC (0x3 is required by DAC) while (SPI2STAT.f1) // wait for SPI module to finish sending asm nop; LATC.CS_PIN = 1; // CS disable for DAC } void InitDec(){ // estimate on 1024 samples for fast DIV SampleCounter = 1024; // init low-freq transitions counter TransitLow = 0; // init high-freq transitions counter TransitHigh = 0; // init input circular buffer (zero-filled) Vector_Set(input, 8, 0); // init filtered circular buffer (zero-filled) Vector_Set(output_f1, 8, 0); // init filtered circular buffer (zero-filled) Vector_Set(output_f2, 8, 0); // points on first element of circular buffer sample_index = 0; // current sign is positive sgnLow = 0; // current sign is positive sgnHigh = 0; DAC_Output(0); } void ADC1Int() org 0x2A { sample = ADCBUF0; // read input ADC signal if ((sample > 2048+MinLevel) && !SignalActive) // detecting signal { SignalActive = 1; // activate estimation algorithm InitDec(); // initialize variables } // since ADC is configured to get samples as intgers // mean value of input signal is expected to be located at // middle of ADC voltage range sample = sample << 4; sample = sample-(2048 << 4); // expanding signal to full scale // now sample is ready to be filtred if (SignalActive) { input[sample_index] = sample; // write sample in circular buffer // filter input signal (for low-freq estimation) sample = IIR_Radix(BPF1_SCALE_B, BPF1_SCALE_A, BPF1_COEFF_B, BPF1_COEFF_A, FILTER_ORDER+1, input, BUFFER_SIZE, output_f1, sample_index); DAC_Output(sample); // output filtred signal to DAC for Visual check output_f1[sample_index] = sample; // transition_Low? if ((sample & 0x8000) != sgnLow) // if transition trough 0 { sgnLow = (sample & 0x8000); // save current sign ++TransitLow; // increment transition counter } // filter input signal (for high-freq estimation) sample = IIR_Radix(BPF2_SCALE_B, BPF2_SCALE_A, BPF2_COEFF_B, BPF2_COEFF_A, FILTER_ORDER+1, input, BUFFER_SIZE, output_f2, sample_index); output_f2[sample_index] = sample; // write filtered signal in buffer // transition_High? if ((sample & 0x8000) != sgnHigh) // if transition { sgnHigh = (sample & 0x8000); ++TransitHigh; // increment transition counter } sample_index = (sample_index+1) & 7; // move pointer on next element --SampleCounter; // decrement sample counter if (SampleCounter == 0) // if all of 1024 samples are readed { SignalActive = 0; // deactivate estimation algorithm Estimate(); // read estimated character DAC_Output(0); // set DAC output to 0 Delay_ms(80); // wait for next char } } IFS0.f11 = 0; // clear ADC complete IF } void Timer1Int() org 0x1A{ ADCON1.f1 = 1; // ASAM=0 and SAMP=1 begin sampling ADCON1.f15 = 1; // start ADC IFS0.f3 = 0; // clear Timer1 IF } void main(){ KeyCnt = 0; // set to 0 SignalActive = 0; // no signal is present ADPCFG = 0xFFFF; // configure pins as digital Lcd_Custom_Config(&PORTD, 7, 6, 5, 4, &PORTB, 4, 2, 6); // initialize LCD Lcd_Custom_Out(1,1,"tone is:"); // print message at first row Lcd_Custom_Cmd(Lcd_SECOND_ROW); // position cursor at second row LATC.CS_PIN = 1; // set DAC CS to inactive LATC.LOAD_PIN = 0; // set DAC LOAD to inactive TRISC.LOAD_PIN = 0; // configure DAC LOAD pin as output TRISC.CS_PIN = 0; // configure DAC CS pin as output // Initialize SPI2 module Spi2_Init_Advanced(_SPI_MASTER, _SPI_16_BIT, _SPI_PRESCALE_SEC_1, _SPI_PRESCALE_PRI_1, _SPI_SS_DISABLE, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_HIGH, _SPI_ACTIVE_2_IDLE); TRISB.f10 = 1; // configure RB10 pin as input ADPCFG = 0xFBFF; // configure RB10 pin as analog ADCON1 = 0x00E0; // auto-convert, auto-conversion ADCON2 = 0x0000; ADCON3 = 0x021A; // sampling time=2*Tad, minimum Tad selected ADCHS = 0x000A; // sample input on RB10 ADCSSL = 0; // no input scan // clear interrupt flags IFS0 = 0; IFS1 = 0; IFS2 = 0; INTCON1 = 0x8000; // disable nested interrupts INTCON2 = 0; IEC0 = 0x0808; // enable Timer1 and ADC interrupts IPC0.f12 = 1; // Timer1 interrupt priority level = 1 IPC2.f13 = 1; // ADC interrupt priority level = 2 // timer1 input clock is Fosc/4. Sampling frequency is 20kHz. Timer should // raise interrupt every 50 microseconds. PR1 = (Fosc[Hz]/4) / 20000Hz = Fosc[kHz]/(4*20) PR1 = Clock_kHz() / 80; T1CON = 0x8000; // Enable Timer1 while(1) // Infinite loop asm nop; }
/* An example of the use of the microcontroller dsPIC30F6014A and Accel Extra Board. The example shows how the signal from the sensor is sampled and how the information on the accelerations along the X and Y axes are used for controlling the cursor on a GLCD. The example also covers the calibration of the sensor (determination of zeroG and 1G values for X and Y axes). Pin RC1 is used as user input. Pull-down PORTC and put button jumper in Vcc position. */ // --- GLCD Messages --- const char msg1[] = "Put board to pos "; const char msg2[] = "and press RC1"; // Global variables signed int zeroG_x, zeroG_y; // zero gravity values signed int oneG_x, oneG_y; // 1G values signed int meas_x, meas_y; // measured values signed int box_x, box_y; // variables for drawing box on GLCD char positionNo; // variable used in text messages char text[21]; // variable used for text messages void Init() { ADPCFG = 0xFCFF; // configure AN8(RB8) and AN9(RB9) as analog pins TRISB.F8 = 1; // configure RB8 and RB9 as input pins TRISB.F9 = 1; Glcd_Init_DsPicPro3(); // init GLCD for dsPICPRO3 board // Note: GLCD/LCD Setup routines are in the setup library files located in the Uses folder // These routines will be moved into AutoComplete in the future. Glcd_Set_Font(FontSystem5x8, 5, 8, 32); // set GLCD font Glcd_Fill(0); // clear GLCD TRISC = 0x02; // pin PORTC.1 is input for calibration positionNo = 1; // variable used in text messages } void DoMeasureXY() { meas_x = Adc_Read(8); // measure X axis acceleration meas_y = Adc_Read(9); // measure Y axis acceleration } void DrawPointerBox() { float x_real, y_real; x_real = (float)(meas_x-zeroG_x)/(oneG_x-zeroG_x); // scale [-1G..1G] to [-1..1] x_real = x_real * 64; // scale [-1..1] to [-64..64] x_real = x_real + 64; // scale [-64..64] to [0..128] y_real = (float)(meas_y-zeroG_y)/(oneG_y-zeroG_y); // scale [-1G..1G] to [-1..1] y_real = y_real * 32; // scale [-1..1] to [-32..32] y_real = y_real + 32; // scale [-32..32] to [0..64] // convert reals to integers box_x = (int)x_real; box_y = (int)y_real; // force x and y to range [0..124] and [0..60] because of Glcd_Box parameters range if (box_x>124) box_x=124; if (box_x<0) box_x=0; if (box_y>60) box_y=60; if (box_y<0) box_y=0; Glcd_Box(box_x, box_y, box_x+3, box_y+3, 2); // draw box pointer, color=2(invert ecah dot) } void ErasePointerBox() { Glcd_Box(box_x, box_y, box_x+3, box_y+3, 2); // draw inverted box at the same position // (erase box) } // --- Calibration procedure determines zeroG and 1G values for X and Y axes ---// void DoCalibrate() { // 1) Put the Accel board in the position 1 : PARALLEL TO EARTH'S SURFACE // to measure Zero Gravity values for X and Y strcpy(text, msg1); text[17] = positionNo + 48; Glcd_Write_Text(text,5,1,1); positionNo++; strcpy(text, msg2); Glcd_Write_Text(text,5,20,1); while (PORTC.F1 == 0) ; // wait for user to press RC1 button DoMeasureXY(); zeroG_x = meas_x; // save Zero Gravity values zeroG_y = meas_y; Delay_ms(1000); // 2) Put the Accel board in the position 2 : X AXIS IS VERTICAL, WITH X LABEL UP // to measure the 1G X value strcpy(text, msg1); text[17] = positionNo + 48; Glcd_Write_Text(text,5,1,1); positionNo++; strcpy(text, msg2); Glcd_Write_Text(text,5,20,1); while (PORTC.F1 == 0) ; // wait for user to press RC1 button DoMeasureXY(); oneG_x = meas_x; // save X axis 1G value Delay_ms(1000); // 3) Put the Accel board in the position 3 : Y AXIS IS VERTICAL, WITH Y LABEL UP // to measure the 1G Y value strcpy(text, msg1); text[17] = positionNo + 48; Glcd_Write_Text(text,5,1,1); positionNo++; strcpy(text, msg2); Glcd_Write_Text(text,5,20,1); while (PORTC.F1 == 0) ; // wait for user to press RC1 button DoMeasureXY(); oneG_y = meas_y; // save Y axis 1G value Delay_ms(1000); } void main() { Init(); // initialization DoCalibrate(); // calibration Glcd_Fill(0); // clear GLCD Glcd_H_Line(0, 127, 32, 1); // draw X and Y axes Glcd_V_Line(0, 63, 64, 1); Glcd_Write_Char('X', 122, 3, 1); Glcd_Write_Char('Y', 66, 0, 1); while (1) { // endless loop DoMeasureXY(); // measure X and Y values DrawPointerBox(); // draw box on GLCD Delay_ms(250); // pause ErasePointerBox(); // erase box } }