我正在使用 PIC18F47-Q18 Curiosity HPC 开发板和 MPLAB X IDE v6.20。
目标是将模拟输出的 10 位值存储到 8 位 PWM 中。取决于模拟对齐的依据。ADRESL 保存前 8 位数据(2^0 至 2^8),ADRESH 保存位 0 和位 1 中的最后两位(2^9 和 2^10)。这样做PWM4DCH = ADRESL;
。当我转动电位器时,LED 会变得更亮。但是,在某个点之后,PWM 循环重新启动,LED 熄灭,然后再次变亮。这重复了 4 次。我相信这是因为我使用的微控制器是 Fosc/4。最终目标是让 LED 在顺时针旋转电位器时达到最大亮度。
我尽可能详细地粘贴了下面的代码。此外,我目前使用轮询,但当我了解它们的工作原理时,我将使用中断标志重新创建它。
// 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;
}
我尝试将 ADRESL 和 ADRESH 设置为整数 x 和 y,然后将它们添加并输出到 PWM:
int x;
int y;
int z;
x = ADRESL;
y = ADRESH << 8;
z = (ADRESH << 8 | ADRESL);
PWM4DCH = z;
使用电位器时,LED 不会做任何事。
我尝试做上面同样的事情,但直接转到PWM4DCH:
PWM4DCH = (ADRESH<<8 | ADRESL);
与上述问题相同。
要将 10 位 ADC 值缩放为 8 位 PWM:
使用公式:
解释:
ADRESH << 6
:将的 2 个 MSBADRESH
与 8 位值的前 2 位对齐。ADRESL >> 2
:保留高6位ADRESL
,丢弃低2位。|
,形成8位缩放结果。您提到的警告是由于从较大的整数类型隐式转换为 8 位无符号类型而导致的。请显式转换以避免这种情况:
(由于我的评论有效,并且您要求提供更多信息,因此我将其放在了答案中。起初我不想这样做,因为我不确定问题是否仅仅是“位处理”问题,或者是特定于我不知道的微控制器的硬件问题)
这看起来很像使用输入的 8 个最低有效位作为 PWM 输出时发生的情况:那么整个范围内就会有 4 个“周期”。
顺便说一下,这似乎正是您的意思
PWM4DCH = ADRESL
。因此,您使用的是 8 个最低有效位。同样,你最后一次尝试只是试图将 10 位(按正确顺序)放入 8 位输出中。 2 个最高有效位可能会被忽略。 你想要做的是
PWM4DCH = (ADRESH<<6) | (ADRESL>>2);
你有(如果我理解正确的话。再次重申,我只是引用你帖子的部分内容,推测我理解它们的意思),
ADRESH
包含******98
和ADRESL
包含76543210
,(使用数字来设计位数。所以 6,表示位 6,即价值 2⁶ 的位)。而你想要的PWM4DCH
是98765432
(删除 1 和 0,因为你不能那么准确)。所以这意味着ADRESH
(从右边的 0 开始编号)位置 1 处的位 9 需要位于位置 7。所以<<6
。而位置 2 处的位 2ADRESL
需要位于结果的位置 0。所以>>2
根据的类型
ADRESL
(如果它是有符号类型),因为>>
运算符不是用填充空白0
,而是用符号位,所以过滤所需的位可能会更安全:PWM4DCH = (ADRESH<<6) | ((ADRESL>>2)&0x3f);
您的警告似乎表明那些(
ADRESH
和ADRESL
)是int
。这有点奇怪(但我认为这不是您选择的),因为显然它们只能容纳 8 位(否则,为什么要将 10 位值拆分为 2 个变量)。您可以通过强制转换来消除警告。PWM4DCH = ((unsigned char)ADRESH<<6) | (((unsigned char)ADRESL>>2)&0x3f);
至于为什么
>>2
,原因与相同<<6
:想想从哪里开始以及希望它在哪里结束。如果您有一个电流计(或任何能够显示一定范围内的值的东西),该电流计由 0 到 9 之间的十进制输入控制,并且想要使用它来显示电池的剩余电量,该电量存储在 00 到 99(以 %)的 2 位十进制数中。那么,显然,您必须删除一位数字并保留另一位数字。最低有效位是您要删除的数字。如果您只保留最后一位,那么当电池充电 39% 时,指示器将显示最大值。然后,当电量下降到 30% 时,指示器会下降到最小值。但紧接着,当电池电量为 29% 时,指示器会跳回最大值,然后当电池电量为 20% 时再次缓慢下降到最小值,然后当电池电量为 19% 时跳回最大值……等等。
从二进制角度来看,这正是当您将输入的最低有效位插入到输出时所发生的情况。
当然,您想要做的是将最高有效数字插入指示器。这样,当电量为 50% 时,指示器位于中间,当电量为 40% 时,指示器会稍微低一些,等等,当电量为 0 到 9% 时,指示器会达到最低值。当然,您会损失一些准确性(指示器无法显示 50 和 59 之间的差异、40 和 49 之间的差异、30 和 39 之间的差异等。但至少,它会不断减少。无论如何,没有其他选择:指示器只有 10 个可能的值,因为它的输入是一个数字)
同样,您要在 10 位 ADC -> 8 位 PWM 中执行的操作是删除 2 个最低位,并使用 8 个最高位作为 PWM 的 8 位。(我确信您已经了解所有这些。但需要清楚)
所以,综上所述,ADRSH 和 ADRSL 中都有这些位
ADRSH
ADRSL
然后你想在 PWM4DCH 中像这样:
PWM4DCH
因此你需要执行以下操作
ADRESH<<6
ADRESL>>2
(ADRSL>>2)&0x3f
因此,当你或(
|
)两者时,你会得到(ADRESH<<6) | (ADRSL>>2)&0x3f