寄存器层中的STM32F4-PWM控制



我正在努力通过ADC值改变LED亮度。实际上,我使用标准外围库做了,它运行良好,但是当我想在寄存器层中编写我的代码时。除了 ST 食谱文档外,我找不到任何文档。所以我编写了食谱引用的代码。它可以工作,但不是你想要的,我可以改变LED的亮度一次。对于LED的连续变化值,我能做些什么? 我的代码如下所示

while (1)
{     
value=ADC_Read();
//PD12,PD13,PD14,PD15 selected for pwm control
pulse_value=map(value,0,4095,0,9999);

TIM4->CCR1|=pulse_value;
TIM4->CCR2|=pulse_value;

TIM4->CCMR1|=0;
TIM4->CCMR1|=0x60;
TIM4->CCMR1|=0x6000;

TIM4->CCER|= 1<<0 | 1<<4;


delay(1680000);

TIM4->CCR3|=pulse_value;
TIM4->CCR4|=pulse_value;

TIM4->CCMR2|=0;
TIM4->CCMR2|=0x60;
TIM4->CCMR2|=0x6000;

TIM4->CCER|= 1<<8 | 1<<12;
TIM4->CR1|=0x1;

delay(1680000);
TIM4->CCER|= 1<<0 | 0<<4| 0<<8 | 0<<12;

因此,当我在三个 LED 上弄乱 PWM 时,我认为这是在 STM32 芯片上(STM32F4将来太模糊了,需要更多的零件号((这是一个STM32F031F4P6(,看起来计时器相似,跨产品重用 IP 并不少见。

PUT32(TIM2_CR1,0x00000000);
PUT32(TIM2_CCR2,200-1);
PUT32(TIM2_CCR3,400-1);
PUT32(TIM2_CCR4,600-1);
PUT32(TIM2_ARR, 800-1);
PUT32(TIM2_CNT,0x00000000);
PUT32(TIM2_PSC,0x00000000);
PUT32(TIM2_CCMR1,(6<<12));
PUT32(TIM2_CCMR2,(6<<12)|(7<<4));
PUT32(TIM2_CCER,(1<<4)|(1<<8)|(1<<12));
PUT32(TIM2_CR1,0x00000001);
while(1)
{
for(ra=0;ra<800;ra++)
{
PUT32(TIM2_CCR2,ra);
PUT32(TIM2_CCR3,ra);
PUT32(TIM2_CCR4,ra);
for(rb=0;rb<2000;rb++) dummy(rb);
}
for(ra--;ra;ra--)
{
PUT32(TIM2_CCR2,ra);
PUT32(TIM2_CCR3,ra);
PUT32(TIM2_CCR4,ra);
for(rb=0;rb<2000;rb++) dummy(rb);
}
}

这是有效的,作为参考(是的,从技术上讲,其中一些是 16 位寄存器,但 st 在这方面很灵活,允许 32 位访问,请注意,无论如何它们都将寄存器间隔在字边界上。

所以这是一个问题

TIM4->CCR1|=pulse_value;
TIM4->CCR2|=pulse_value;

你想用那里的东西替换位而不是ORR,最终它们将是所有的

TIM4->CCR1 = (TIM4->CCR1)&(~0xFFFF)|pulse_value;

但实际上,由于寄存器没有分区,因此它是一个字段/值

TIM4->CCR1 = pulse_value;

这将是正确的做法。

这当然没有任何作用:

TIM4->CCMR1|=0;

你的意思是

TIM4->CCMR1=0;

? 为什么要单独执行字段(作为一种习惯,并且每个读取-修改-写入的外围设备都发生变化,只需一次设置所需的寄存器位,无论是一次读取-修改-写入还是一次写入(。 作为一般规则,在这样的单独操作中对外围设备进行场是不安全的。 而且,您不能只或等于寄存器而不清除字段以清除它。

Bits 6:4 OC1M: Output compare 1 mode
110: PWM mode 1

TIM4->CCMR1|=0x60;

相反,该字段需要这样的东西

TIM4->CCMR1 = (TIM4->CCMR1&(~(7<<4((( |(6<<4(;

我发现它更具可读性

x = TIM4->CCMR1;
x&=(~7)<<4;
x|=   6<<4;
TIM4->CCMR1 = x;

甚至更好

Bits 14:12 OC2M[2:0]: Output compare 2 mode
Bits 6:4 OC1M: Output compare 1 mode
x = TIM4->CCMR1;
x&=(~7)<<12;
x|=   6<<12;
x&=(~7)<<4;
x|=   6<<4;
TIM4->CCMR1 = x;

我怀疑你真正想做的是:

TIM4->CCMR1=0;
TIM4->CCMR1|=0x6060;

寄存器重置为0x0000

TIM4->CCMR1=0x6060;

会没事的。 或为了可读性

TIM4->CCMR1=(6<<12)|(6<<4);

您可以直接从代码中的参考手册中看到 12 和 4 以及 6(110 二进制(。

此外,我相信(您可以像我一样阅读文档(您只需要设置 pwm 一次,因此所有寄存器在 init 时只执行一次,然后对于您从 adc 读取的每个新值,您只需更改比较寄存器值。 在该周期或下一个周期,pwm 将相对于新的寄存器值更改状态(取决于您相对于当前周期写入它的时间(。

我想你已经很接近了,你明白了,只需研究你的 AND、OR、XOR 和 NOT 技能(按位逻辑运算(。

在处理 gpio 寄存器时,引脚 A3 可能是小部件 B 的一些控制信号,A5 可能是小部件 X 的一些控制信号,并且您希望为每个小部件(一些外部外设或 LED 或按钮或其他任何东西(使用单独的软件,然后读取-修改-写入(正确完成(很有意义,具体取决于该芯片的 gpio 外设的逻辑如何工作。 但是,如果您要配置uart,定时器,spi控制器等。 如果您需要一次性控制寄存器中的所有位,您用一个驱动程序拥有整个东西,那么读取-修改-写入不一定有意义。 只需将整个寄存器写入该配置所需的所有位,只要寄存器访问即可。 为了代码的可读性(相对术语,美在旁观者的眼中,对每个他(/她(自己的等(,您可以选择只编写整个事情的十六进制值,做一些移动,以便您可以轻松地将值转换为参考手册,以帮助减少错误。 创建许多对变量进行位操作的宏,以便您可以真正将参考手册中的值插入到代码中(SET_BITS(x,16,14,6(;SET_BITS(x,6,4,6(;)等。

您的主循环最终可能看起来像

while (1)
{     
value=ADC_Read();
//PD12,PD13,PD14,PD15 selected for pwm control
pulse_value=map(value,0,4095,0,9999);
TIM4->CCR1=pulse_value;
TIM4->CCR2=pulse_value;
TIM4->CCR3=pulse_value;
TIM4->CCR4=pulse_value;
delay(1680000);
}

前面的设置是 TIM 中需要写入以使其工作的其余寄存器。 可能为CCR1寄存器或某种起点使用较小的值,或者执行ADC读取,然后使用该ADC读取pulse_value进行设置,然后进入while循环。

最新更新