Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] Led VU Meter, How can I improve it ?

Status
Not open for further replies.

Okada

Banned
Joined
Jun 16, 2016
Messages
1,159
Helped
129
Reputation
252
Reaction score
129
Trophy points
63
Activity points
0
This is my PIC18F based Led VU Meter (16 channel bargraph). How can I improve the code ?

This is my code. Proteus simulation, mikroC PRO PIC project and Proteus Simulation video are attached. In simulation change the .wav to any 8 KHz wave file.

I have used multiplication and division operators in the equation. Can I replace them with shift operators in someway to get the same result ?

I have also done another version which uses LUT and it is simple but I wanted to do it differently.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#define MAX_ADC_VALUE 1023
 
unsigned char number_of_bars = 16;
unsigned long int raw_adc_value = 0, vu_meter_data = 0;
char select_channel = 0;
 
void Interrupt() {
    if((ADIE_bit) && (ADIF_bit)) {
        //read raw adc value
        raw_adc_value = (unsigned int)ADRESH << 8 | ADRESL;
        
        //convert raw adc value to bargraph data
        //slow method
        vu_meter_data = ((2 << ((raw_adc_value * number_of_bars) / MAX_ADC_VALUE)) - 1);
 
        if(select_channel == 0) {
            CHS0_bit = 0;
            CHS1_bit = 1;
            CHS2_bit = 1;
            CHS3_bit = 0;
            CHS4_bit = 0;
 
            LATA = vu_meter_data;
            LATB = vu_meter_data >> 8;
        }
        else if(select_channel == 1) {
            CHS0_bit = 1;
            CHS1_bit = 0;
            CHS2_bit = 1;
            CHS3_bit = 0;
            CHS4_bit = 0;
            
            LATC = vu_meter_data;
            LATD = vu_meter_data >> 8;
        }
        
        if(++select_channel > 1)select_channel = 0;
        
        ADIF_bit = 0;
        GO_DONE_bit = 1;
    }
}
 
void main() {
 
    asm clrwdt
 
    OSCCON = 0x67;
    OSCCON2 = 0x83;
    OSCTUNE = 0x00;
    
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
    
    SLRCON = 0x00;
    
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x03;
    
    ADCON1 = 0x80;
    ADCON2 = 0b10110110;
    
    TRISA = 0x00;
    TRISB = 0x00;
    TRISC = 0x00;
    TRISD = 0x00;
    TRISE = 0x03;
    
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
    
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
    
    Delay_ms(100);
 
    ADIE_bit = 1;
    PEIE_bit = 1;
    GIE_bit = 1;
    
    CHS0_bit = 1;
    CHS1_bit = 0;
    CHS2_bit = 1;
    CHS3_bit = 0;
    CHS4_bit = 0;
    
    ADON_bit = 1;
    GO_DONE_bit = 1;
    
    while(1) {
         asm clrwdt
        
         LATE2_bit = ~LATE2_bit;
         Delay_ms(500);
    }
}



- - - Updated - - -

What is the speed of 74HC595 SH_CP at 5V working voltage ? 25 MHz ?

If I use 32 MHz Oscillator for PIC then instrction clock will be fosc / 4 = 32 MHz / 4 = 8 MHz.

Instruction time will be 1 / 8 MHz = 0.125 us.

If 74HC595 can work at 25 MHz then SH_CP speed will be 0.04 us

So, can I clock 74HC595 with 0.125 us speed ?

I am making a shift register based VU Meter which needs 6 pins of a microcontroller.
 

Attachments

  • VU Meter Using Interrupts - Version 5.rar
    326.8 KB · Views: 125
Last edited:

On page 10 it talks about the speed of the clocks under Fmax. The frequency depends on temperature and voltage of the supply. So I would say that you can do 20MHz min on 5V at any temperature range. Now more than likely you could go much faster, but the datasheet only discusses 4.5 and 6V. My guess is you would just be a slightly above 4.5V. I do not know the temperature that this will be used in, but in any case you will be able to run faster than 8MHz which is the fastest your microcontroller appears to be able to push. I think the PIC18F can go up to 16MHz on certain chips, which is still okay.

https://assets.nexperia.com/documents/data-sheet/74HC_HCT595.pdf

The multiply can be replaced with a left shift of 4. Since it is an even power of two you can use left shift in place of multiply. (The compiler may have done this for you!) The same is true for division using right shift, however 1023 is not an even power of 2, but 1024 is. So you try replacing it with a right shift by 10. (This will slightly change the result, but I am not sure it would be significant.)

You could drive a bargraph with a 74HC595 shift register, but it would be faster and simpler to use parallel like you are with the port register.
 
Last edited:

I am using mikroC PRO PIC Compiler.

Ok. I will try using shift operators for multiplication and division.

Here is the 74HC595 based Led VU Meter. It is not working fine in Proteus. maybe processor is overloaded. I have to test it on hardware.

Delay between reading different adc channels is not implemented. I guess the delay generated during shifting the bargraph data will be sufficient.

I think I have to make it slightly differently like first read both the adc channels using adc interrupt and then use timer interrupt to shift data of both channels at the same time. I think this will be more responsive. I will try it.

Edit:

Another think I want to implement is find out the bar size i.e., for different adc value the number of active bars will be different and so if bar size is found then only bar size data can be shifted to 74HC595 and so this will make the display more responsive.

Eg: if bar data is 0xF then only 4 bits need to be shifted and so the mask value and bar size need to be adjusted.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#define MAX_ADC_VALUE 1023
 
sbit DS1 at LATB0_bit;
sbit DS2 at LATB1_bit;
sbit SH_CP at LATB2_bit;
sbit ST_CP at LATB3_bit;
 
unsigned int raw_adc_value = 0;
unsigned long int vu_meter_data = 0;
unsigned char number_of_bars = 16;
char shift_counter = 0;
char select = 0, i = 0;
unsigned int mask = 0x8000;
 
//Timer1
//Prescaler 1:1; TMR1 Preload = 64456; Actual Interrupt Time : 10 us
void InitTimer1() {
    T1CON = 0x01;
    TMR1IF_bit = 0;
    TMR1H = 0xFF;
    TMR1L = 0xB0;
    TMR1IE_bit = 1;
    INTCON = 0xC0;
}
 
void Interrupt() {
    if((ADIE_bit) && (ADIF_bit)) {
        raw_adc_value = (unsigned int)ADRESH << 8 | ADRESL;
        vu_meter_data = ((2 << ((raw_adc_value * number_of_bars) / MAX_ADC_VALUE)) - 1);
 
        ADIE_bit = 0;
        ADIF_bit = 0;
        
        InitTimer1();
    }
    
    if((TMR1IE_bit) && (TMR1IF_bit)) {
        if(mask & vu_meter_data) {
           if(select == 0) {
             DS1 = 1;
           }
           else if(select == 1) {
             DS2 = 1;
           }
        }
        else {
            if(select == 0) {
             DS1 = 0;
           }
           else if(select == 1) {
             DS2 = 0;
           }
        }
 
        SH_CP = 1;
        SH_CP = 0;
 
        mask >>= 1;
 
        if(++shift_counter >= number_of_bars) {
            ST_CP = 1;
            ST_CP = 0;
 
            mask = 2 << (number_of_bars - 2);
 
            if(++select > 1) {
               select = 0;
            }
 
            if(select == 0) {
                CHS0_bit = 0;
                CHS1_bit = 0;
            }
            else if(select == 1) {
                CHS0_bit = 1;
                CHS1_bit = 0;
            }
            
            ADIE_bit = 1;
            ADIF_bit = 0;
            GO_DONE_bit = 1;
            
            shift_counter = 0;
            
            TMR1IE_bit = 0;
        }
        
        TMR1IF_bit = 0;
        TMR1H = 0xFF;
        TMR1L = 0xB0;
    }
}
 
void main() {
 
    asm clrwdt
 
    OSCCON = 0x77;
    OSCTUNE = 0xC0;
 
    CMCON = 0x00;
    CVRCON = 0x00;
 
    ADCON1 = 0x0C;
    ADCON2 = 0b10110110;
 
    TRISA = 0x03;
    TRISB = 0x00;
 
    PORTA = 0x00;
    PORTB = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
 
    Delay_ms(100);
 
    ADIE_bit = 1;
    PEIE_bit = 1;
    GIE_bit = 1;
 
    CHS0_bit = 1;
    CHS1_bit = 0;
 
    ADON_bit = 1;
    GO_DONE_bit = 1;
 
    while(1) {
         asm clrwdt
    }
}

 

Attachments

  • Led VU Meter Using PIC18F1330, 74HC595 and Interrupts - Revision A.rar
    271.6 KB · Views: 66
Last edited:

I tried to make the better version of VU Meter using shift register. Now data of both channels are shifted to shift registers with the same clock. Timer interrupt period is 10us and so max 160 us time is needed to shift data to shift register if there are 16 bars in bargraph data.

If you find any bugs then please report it so that I can fix it and make the code better.

There is one bug related to these.


Code C - [expand]
1
2
vu_meter_data[channel_counter] = ((2 << number_of_active_bars[channel_counter]) - 1);
mask[channel_counter] = 2 << (number_of_active_bars[channel_counter] - 2);



If number of active bars is 0 then left shift value will be negative and so there will be no shifting but how can I avoid this condition ? Should I use

Code:
if(number_of_active_bars[channel_counter]) {
    vu_meter_data[channel_counter] = ((2 << number_of_active_bars[channel_counter]) - 1);
    mask[channel_counter] = 2 << (number_of_active_bars[channel_counter] - 2);
}

?


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#define MAX_ADC_VALUE 1023
#define Channel_Size 2
 
sbit DS1 at LATB0_bit;
sbit DS2 at LATB1_bit;
sbit SH_CP at LATB2_bit;
sbit ST_CP at LATB3_bit;
sbit MR at LATB4_bit;
sbit OE at LATB5_bit;
 
unsigned int raw_adc_value[2] = {0, 0};
unsigned long int vu_meter_data[2] = {0, 0};
unsigned char number_of_bars = 16;
char shift_counter = 0;
char channel_counter = 0;
char number_of_active_bars[2] = {0, 0};
unsigned int mask[2] = {0 , 0};
char bigger_bar_index = 0;
 
//Timer1
//Prescaler 1:1; TMR1 Preload = 65456; Actual Interrupt Time : 10 us
void InitTimer1() {
    T1CON = 0x01;
    TMR1IF_bit = 0;
    TMR1H = 0xFF;
    TMR1L = 0xB0;
    TMR1IE_bit = 1;
}
 
void Interrupt() {
    if((ADIE_bit) && (ADIF_bit)) {
        ADIF_bit = 0;
        
        raw_adc_value[channel_counter] = (unsigned int)ADRESH << 8 | ADRESL;
        number_of_active_bars[channel_counter] = ((raw_adc_value[channel_counter] * number_of_bars) / MAX_ADC_VALUE);
        vu_meter_data[channel_counter] = ((2 << number_of_active_bars[channel_counter]) - 1);
        mask[channel_counter] = 2 << (number_of_active_bars[channel_counter] - 2);
        
        if(++channel_counter > 1) {
            channel_counter = 0;
            
            ADIE_bit = 0;
 
            MR = 0;
            MR = 1;
            
            if(number_of_active_bars[0] > number_of_active_bars[1]) {
               bigger_bar_index = 0;
            }
            else {
               bigger_bar_index = 1;
            }
            
            InitTimer1();
        }
        else {
            if(channel_counter == 0) {
                CHS0_bit = 0;
                CHS1_bit = 0;
            }
            else if(channel_counter == 1) {
                CHS0_bit = 1;
                CHS1_bit = 0;
            }
 
            GO_DONE_bit = 1;
        }
    }
    
    if((TMR1IE_bit) && (TMR1IF_bit)) {
        for(channel_counter = 0; channel_counter < Channel_Size; channel_counter++) {
            if(mask[bigger_bar_index] & vu_meter_data[channel_counter]) {
                if(channel_counter == 0) {
                   DS1 = 1;
                }
                else if(channel_counter == 1) {
                   DS2 = 1;
                }
            }
            else {
               if(channel_counter == 0) {
                  DS1 = 0;
               }
               else if(channel_counter == 1) {
                  DS2 = 0;
               }
            }
 
            mask[channel_counter] >>= 1;
        }
 
        SH_CP = 1;
        SH_CP = 0;
        
        if(++shift_counter >= number_of_active_bars[bigger_bar_index]) {
            TMR1IE_bit = 0;
            
            ST_CP = 1;
            ST_CP = 0;
 
            shift_counter = 0;
            channel_counter = 0;
            
            ADIE_bit = 1;
            ADIF_bit = 0;
            CHS0_bit = 0;
            CHS1_bit = 0;
            GO_DONE_bit = 1;
        }
        
        TMR1IF_bit = 0;
        TMR1H = 0xFF;
        TMR1L = 0xB0;
    }
}
 
void main() {
 
    asm clrwdt
 
    OSCCON = 0x77;
    OSCTUNE = 0xC0;
 
    CMCON = 0x00;
    CVRCON = 0x00;
 
    ADCON1 = 0x0C;
    ADCON2 = 0b10110110;
 
    TRISA = 0x03;
    TRISB = 0x00;
 
    PORTA = 0x00;
    PORTB = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
 
    Delay_ms(100);
 
    MR = 1;
    
    ADIE_bit = 1;
    PEIE_bit = 1;
    GIE_bit = 1;
 
    CHS0_bit = 0;
    CHS1_bit = 0;
 
    ADON_bit = 1;
    GO_DONE_bit = 1;
 
    while(1) {
         asm clrwdt
    }
}

 

Attachments

  • Led VU Meter Using PIC18F1330, 74HC595 and Interrupts - Version 2.rar
    287.9 KB · Views: 66
Last edited:

Why are you making this so fast? 10usec interrupts? The LED display does not have to be so fast because the human eye takes msec to notice.
You need peak-hold averaging on the A/D and LED display.

EPE magazine has the 80-led PIC32MX VU meters 100dB project, maybe look there.
 
  • Like
Reactions: Okada

    Okada

    Points: 2
    Helpful Answer Positive Rating
Ok. I also have to modify the code so that adc is not read unnesassarily. I need to read adc according to audio frequency. The controller is dedicated to VU Meter. it will not do any other task.
 

Sampling theory says that you must sample the signal at twice the maximum frequency you want to measure. If you are looking at really high fidelity sound then that might go to 15KHz (even through typical hearing is limited to below that, especially for anyone over about 20). On the other hand, communications (telephones, radios) typically occurs in the range from 300Hz to 4KHz.
One you have the samples and process them. How fast do you want the system to react to a sharp rise and fall sound such as a clap? How about sustained music? Do you want to mimic the human hearing volume response or do you want to show the average sound with, perhaps, a peak of the last few seconds.
Then you need to think about how fast to update the display. As mentioned above the human eye can detect about 20Hz (which is why movies have a frame rate of 25HZ and above to appear smooth) so updating the display very much faster than that may be a waste of time. The LEDs can respond but the person looking at it cannot see the difference.
Technology can probably do just about anything you need it to, but you must always remember the context in which it is operating and the problem domain it is working in.
Susan
 
  • Like
Reactions: Okada

    Okada

    Points: 2
    Helpful Answer Positive Rating
Hi,

I need to read adc according to audio frequency.
In this case ... I'd say it is no "must".
You don't need the frequency information, and thus you don't need to care if you get the true audio frequency or the alias frequencies.

Klaus
 
  • Like
Reactions: Okada

    Okada

    Points: 2
    Helpful Answer Positive Rating
Power spectral content of music at >16kHz is quite low, so you can undersample with small accuracy loss.
I'm used to seeing an active-rectifier with small filter feeding the A/D for VU meters. That way you can (hardware) filter down to say a few kHz and lessen the MCU requirements.

PIC18F has single 10-bit A/D which needs min. 1.7usec to do a conversion. OP says 16-channels so I don't think you can sample the A/D at 40kHz or 25usec.
OP is shifting-out LED data in the A/D sample interrupt which is wasting too much MCU resources. My main point is the LED display thread can be slow and update 1,000x slower.
 

It is not 16 channels. It is 16 bars in bargraph display. ADC is 10 bit. Audio channels is 2.
 

Ok 16-LEDS for 2 channels? what is your hardware feeding ADC, music is AC not DC.
 

Yes, 16 Leds for each channel and 2 channels (L and R - stereo). I have not yet built the hardware. I have to design the PCB layout and get it fabricated.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top