I'm using the PIC18F47-Q18 Curiosity HPC Development Board and the MPLAB X IDE v6.20.
The goal is to store the 10 bit value from the analog output into the 8 bit PWM. Depending on the justification of the analog alignment. ADRESL holds the first 8 bits of data (2^0 up to 2^8), and ADRESH holds the last two bits in bit 0 and bit 1 (2^9 and 2^10). By doing PWM4DCH = ADRESL;
. The LED will get brighter when I turn the potentiometer. However, after a certain point the PWM cycle restarts and the LED turns off before getting brigher again. This repeats 4 times. I believe this is due to the fact that the microcontroller I am using is Fosc/4. The final goal is to make the LED reach max brightness when the potentiometer is turned clockwise.
I pasted the code below with as much detail and information as possible. Additionally, I currently use polling but I will recreate this using the interrupt flags when I learn how they work.
// CONFIG1L
#pragma config FEXTOSC = OFF
#pragma config RSTOSC = HFINTOSC_64MHZ
// CONFIG1H
#pragma config CLKOUTEN = OFF
#pragma config CSWEN = OFF
#pragma config FCMEN = OFF
// CONFIG3L
#pragma config WDTCPS = WDTCPS_31
#pragma config WDTE = OFF
#define clock OSCCON1 // define the timer control bits
#define freq OSCFRQ
#define enable_timer T2CONbits.T2ON // define the timer control bits
#define interrupt_enable PIE4bits.TMR2IE
#define PB1 PORTBbits.RB4
#define PB2 PORTCbits.RC5
#define light PORTAbits.RA7
void main(void) {
clock = 0x60; // use High Mhz clock
freq = 0x03; // Use 8Mhz clock
T2CLKCON = 0x01; // Make Timer2 Fosc/4
ANSELA = 0x01; // set up port A,B,C D/A
TRISA = 0x01; // set up port A,B,C I/O
ANSELB = 0x00;
TRISB = 0xFF;
ANSELC = 0x00;
TRISC = 0xFF;
T2PR = 0xC7; // period of the PWM
PWM4DCH = 0x95; // set up duty cycle of PWM
PWM4DCL = 0x10; // set up duty cycle of PWM
RA4PPS = 0x08; // set RA4 to be PWM output
T2CON = 0x20; // set pre scale value of 1:4
PWM4CONbits.PWM4EN = 1; // enable the PWM
PWM4CONbits.POL = 1; // reverse Polarity of PWM
while (1)
{
enable_timer=1; // enable the timer 2
while(PIR4bits.TMR2IF==0); // Monitor that the interrupt flag is zero
enable_timer=0; // disable the timer
PIR4bits.TMR2IF=0; // clear the interrupt flag
ADCON0 = 0x84; // ADC is enabled and ADFM is right justified
ADPCH = 0x00; // Selected channel is RA0
ADCON0bits.GO =1; // start ADC conversion
while (ADCON0bits.GO ==0); // using polling method to wait until GO bit is 0
PWM4DCH = ADRESL; // display ADRESL results on PWM4DCH
ADCON0bits.ADON = 0; // Turn off ADC enable bit
}
return;
}
I tried to set ADRESL and ADRESH into a integer x and y before adding them and outputing to the PWM:
int x;
int y;
int z;
x = ADRESL;
y = ADRESH << 8;
z = (ADRESH << 8 | ADRESL);
PWM4DCH = z;
LED would not do anything when the potentiometer is used.
I tried to do the same thing above, but go directly to PWM4DCH:
PWM4DCH = (ADRESH<<8 | ADRESL);
Same issue as above.
(Since my comment works, and you asked for more information, I put this in an answer. I didn't want to do it at first, because I wasn't sure if the problem was just that "bits handling" problem, or a hardware problem specific to a microcontroler I don't know)
This looks a lot like what would happen if you use the 8 least significant bits of input as PWM output: then you would have 4 "cycles" in the entire range.
Which incidently seems to be exactly what you meant by PWM4DCH = ADRESL
. So, you are using the 8 least significant bits.
Your last attempt, likewise, is just trying to put 10 bits (in the correct order) into a 8 bits output. The 2 most significant bits are probably ignored. What you meant to do is rather PWM4DCH = (ADRESH<<6) | (ADRESL>>2);
You have (if I follow correctly. Again, I am just citing parts of your post, surmising I understand what they mean), ADRESH
containing ******98
and ADRESL
containing 76543210
, (using digits to designed bit number. So 6, means bit 6, the one that worth 2⁶). And you want PWM4DCH
to be 98765432
(droping 1 and 0, since you can't be that accurate). So that means that bit 9 at position 1 of ADRESH
(starting numbering at 0 on the right), needs to be at position 7. So <<6
. And bit 2 at position 2 of ADRESL
needs to be at position 0 of the result. So >>2
Depending on the types of ADRESL
(if it is a signed type), since >>
operator fills the blanks not with 0
, but with the sign bit, it may be safer to filter the bits you want:
PWM4DCH = (ADRESH<<6) | ((ADRESL>>2)&0x3f);
Your warning seems to indicate that those (ADRESH
and ADRESL
) are int
. That's a bit strange (but I take that is not of your choosing), since clearly, they can hold only 8 bits (otherwise, why split that 10 bits value in 2 variables). You can get rid of the warning by casting.
PWM4DCH = ((unsigned char)ADRESH<<6) | (((unsigned char)ADRESL>>2)&0x3f);
As for why the >>2
, well same reason as for the <<6
: think of from where the bit start and where you want it to end up.
If you had a galvanometer (or anything able to show a value in a range) that was controlled by a decimal input between 0 and 9, and wanted to use it to display the remaining charge of a battery, that is stored into a 2 digit decimal number from 00 to 99 (in %). Then, obviously, you will have to drop a digit and keep another. The least significant digit is the one you want to drop. If you kept only the last one, then, when battery is charged 39%, the indicator shows maximum value. Then when charge drops at 30%, indicator drops at minimum value. But immediately after, when battery is 29%, indicator jump back to max, then slowly decrease to the min again when battery is 20%, then jump back to max when battery is 19%... etc.
This is exactly, in binary, what happened when you plugged the least significant bits of your input to your output.
Of course, what you want to do, is to plug the most significant digit to your indicator. So that when charge is 50%, indicator is in the middle, then a bit lower when charge is 40%, etc, and reach minimum when charge is 0 to 9%. Of course, you loose some acuracy (the indicator is unable to show the difference between 50 and 59, between 40 and 49, between 30 and 39, etc. But at least, it decreases continuously. And anyway, there is not other choice: indicator has only 10 possible values, since its input is a single digit)
Likewise, what you want to do in your 10 bits ADC -> 8 bits PWM, is to drop the 2 lowest bits, and use the 8 highest one as the 8 bits of the PWM. (I am pretty sure you understand already all this. But need to be clear)
So, all that being said you have those bits in ADRSH and ADRSL
value | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
ADRSH |
? | ? | ? | ? | ? | ? | b9 | b8 |
ADRSL |
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
And you want then in PWM4DCH like this :
value | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
PWM4DCH |
b9 | b8 | b7 | b6 | b5 | b4 | b3 | b2 |
So you need to perform the following operations
value | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
ADRESH<<6 |
b9 | b8 | 0 | 0 | 0 | 0 | 0 | 0 |
ADRESL>>2 |
? | ? | b7 | b6 | b5 | b4 | b3 | b2 |
(ADRSL>>2)&0x3f |
0 | 0 | b7 | b6 | b5 | b4 | b3 | b2 |
So that when you or (|
) the two, you get
value | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
(ADRESH<<6) | (ADRSL>>2)&0x3f |
b9 | b8 | b7 | b6 | b5 | b4 | b3 | b2 |