为什么我不能更改 ATTINY1616 中的 PWM 输出引脚?

Col*_*ck6 4 c microcontroller avr atmelstudio attiny

我已经开始深入研究 MCU 编程并一直在使用一些基本代码。我首先使用 Arduino 完成此操作,但现在我尝试使用 ATTINY1616 MCU,并且我对如何创建使 LED 调暗和关闭的 PWM 效果感到困惑。我以为我有一个有效的代码,但由于某种原因,它只能与一个引脚一起工作。我已阅读数据表,它确认有更多引脚可用于 PWM,并且没有发现我可能会错过使用其他引脚的命令或语句。

\n

无论我选择哪个引脚作为输出,代码都会编译,但在 MCU 中,PB0 是唯一可以工作的引脚。其他引脚不会执行任何操作,不会给出任何输出,并且软件(Microship Studio)不会显示任何错误。我什至把 MCU 换成了新的。

\n

我\xe2\x80\x99m 不太擅长阅读数据表,但我真的没有\xe2\x80\x99t 看到任何专门允许某些引脚成为 PWM 的命令(https://ww1.microchip.com/downloads/en/DeviceDoc /ATtiny1614-16-17-DataSheet-DS40002204A.pdf,特别是 TCA 的第 20 节)。我的理论是,由于 PB0 显示为 TCA0 的 WO0,因此该引脚就像 TCA 功能的默认引脚,我需要一个命令来指示我想使用另一个引脚,但我无法\xe2\x80\x99 找到任何有效的引脚。

\n
#define F_CPU 20000000\n#include <avr/io.h>\n#include <avr/interrupt.h>\n#include <util/delay.h>\n#include <stdint.h>\n\n\n\nvoid PWM_init() {\n    // Configure Timer/Counter 0 (TC0) for PWM generation\n\n    // Set TC0 waveform generation mode to Fast PWM\n    TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_SINGLESLOPE_gc | TCA_SINGLE_CMP0EN_bm;\n\n    // Set TC0 period (TOP value) to 8-bit resolution\n    TCA0.SINGLE.PER = 0xFF;\n\n    // Set TC0 PWM output on PB0 (LED pin)  \n    PORTB.DIRSET = PIN0_bm;\n    //PORTA.DIRSET = PIN4_bm; <------Pin PA4. It won\'t work even if PB0 is removed\n\n    TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm;\n}\n\nvoid PWM_control() {\n    // Define the number of steps and the delay between each step\n    const uint16_t numSteps = 256;\n    const uint16_t delayMs = 10;\n\n    // Fade in\n    for (uint16_t step = 0; step < numSteps; step++) {\n        // Set ADC value based on the step\n        uint16_t adcValue = step;\n\n        // Scale the ADC value to PWM range (0-255)\n        uint8_t pwmValue = adcValue >> 2; // Right shift by 2 (divide by 4)\n\n        // Update PWM duty cycle\n        TCA0.SINGLE.CMP0 = pwmValue;\n\n        // Delay between each step\n        _delay_ms(delayMs);\n    }\n    // Fade out\n    for (uint16_t step = 256; step > 0; step--) {\n        // Set ADC value based on the step\n        uint16_t adcValue = step;\n\n        // Scale the ADC value to PWM range (0-255)\n        uint8_t pwmValue = adcValue >> 2; // Right shift by 2 (divide by 4)\n\n        // Update PWM duty cycle\n        TCA0.SINGLE.CMP0 = pwmValue;\n\n        // Delay between each step\n        _delay_ms(delayMs);\n    }\n}\n\nint main() {\n    // Initialize PWM\n    PWM_init();\n    CPU_CCP = 0xD8; //enable write to protected register;\n    CLKCTRL_MCLKCTRLB = 0; // No prescaling to periphery, therefore 20MHz frequency;\n\n    while (1) {\n        // Control PWM\n        PWM_control();\n\n        _delay_ms(10);\n    }\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

试过:

\n
    \n
  • 在MCU上使用Arduino版本的代码,它可以工作。
  • \n
  • 简单地将\'PORTx.DIRSET\'更改为任何其他都不起作用,仅在PB0中
  • \n
  • 另一个不起作用的引脚的单独无效函数
  • \n
  • 更改一些命令如“TCA_SINGLE_CMP0EN_bm”、“TCA_SINGLE_WGMODE_SINGLESLOPE_gc”,从TCA更改为TCB。没有任何效果
  • \n
\n

E:对 PWM 设置中的解决方案进行了更改

\n
    TCA0.SPLIT.CTRLA = TCA_SINGLE_ENABLE_bp;\n    //SPLIT MODE INIT\n    TCA0.SPLIT.CTRLD = TCA_SINGLE_SPLITM_bm;\n    TCA0.SPLIT.HPER = 0xFF;\n    TCA0.SPLIT.CTRLA = TCA_SINGLE_ENABLE_bm;\n    TCA0.SPLIT.CTRLB = TCA_SPLIT_HCMP0EN_bm | TCA_SPLIT_HCMP1EN_bm | TCA_SPLIT_HCMP2EN_bm;\n
Run Code Online (Sandbox Code Playgroud)\n

在控件中,我只是将引脚 PA4 的 TCA0.SINGLE.CMP0 更改为 TCA0.SPLIT.HCMP1。

\n

Mat*_*t S 5

是的,每个 PWM 外设都与其自己的特定引脚相关联,如数据表中所述。ATtiny 中的外设与常规通用输入输出 (GPIO) 引脚复用,这意味着如果您不使用外设,则可以将其引脚用作 GPIO。反之则不然:您不能简单地将任何外设连接到任何引脚,因为从根本上来说,这些电线并不存在。每个(非电源)引脚都是 GPIO,因为这是一个非常有用的功能,但是将每个外设连接到每个引脚将需要硅芯片上非常复杂的相互缠绕的电线网络!

您可以在数据表(第 18 页)第 5.1 节中找到一个总结多路复用的表格。您正在使用TCA0.WO0与 复用的PB0. 不过,我请您注意注释 3:可以通过端口多路复用器选择备用引脚位置。具体还可以TCA0.WO0通过PB3. 您也不限于WO0;如果您正确设置了定时器外设,您可以在标题下列出的任何引脚上启动自由运行的 PWM TCA0

从根本上来说,PWM 是在主程序循环之外的存储器映射外设中实现的。具体来说,它是一个带有一组比较寄存器的定时器外设:定时器不断计数,一旦计数等于比较寄存器的内容,就会发生一些事情。在 PWM 的情况下,一个引脚变低。(当计数器归零时,它会变高。)

要按照您想要使用的方式初始化 PWM,您需要写入各种内存地址(因此称为“内存映射”)。您正在您的PWM_init()函数中执行此操作。一旦您说“开始”(通过设置位CTRLA.ENABLE),外设就会永远自行运行。

要更详细地了解外设的工作原理,您需要转到数据表中的第 20 章。从该部分,我看到这行代码:

 TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_SINGLESLOPE_gc | TCA_SINGLE_CMP0EN_bm;
Run Code Online (Sandbox Code Playgroud)

正在将值写入CTRLB寄存器(请参见第 190 页)。您已切换该CMP0EN标志,启用与输出 对应的引脚上的比较功能WO0。因此,PWM 接管PB0并使用它的功能WO0。(您还将 PWM 设置为“单斜率”模式;有关详细信息,请参阅数据表的第 182 页。)

然后在您的PWM_control()函数中写入寄存器CMP0

        // Update PWM duty cycle
        TCA0.SINGLE.CMP0 = pwmValue;
Run Code Online (Sandbox Code Playgroud)

它允许您设置占空比。从根本上讲,只要定时器翻转到零,您的 PWM 输出就会切换为高电平,一旦定时器等于比较寄存器的内容,就会再次切换回低电平。以下是数据表(第 183 页)中的相关时序图:

计数和比较寄存器的时序图。

相反,如果您想在不同的引脚(例如 PB1)上看到 PWM 输出,则首先将 PB1 设置为输出,然后设置寄存器CMP1EN中的使能位CTRLB。(大概你的 IDE 已经TCA_SINGLE_CMP1EN_bm定义了,但也可能没有;如果是这样,只需适当编辑该行即可。)你还可以阅读有关端口多路复用器的部分(第 15 章),它使你可以访问寄存器以启用备用输出WO0,这会将 PWM 信号打开PB3

但请注意,虽然您所需的引脚 PA4 可以产生 PWM 信号,但它只能在分离模式下产生,这需要对 PWM 外设进行不同的设置。你可以做到这一点,但你需要深入研究第 20 章来弄清楚如何在分离模式下设置 PWM 外设。

总之,您可以相当轻松地将 PWM 更改为在引脚PB1或上输出PB2:您所要做的就是使用它们的匹配CMPn寄存器,并CMPnEN在 中写入适当的位CTRL2。稍微复杂一点,您可以写入寄存器PORTMUX.CTRLC,并将 PWM 路由到 PB3、PB4 或 PB5。通过更多的努力,您可以启用分割模式并在 PA3、PA4 或 PA5 上获得 PWM,或者通过执行分割模式并写入端口多路复用器,您可以在 PC3、PC4 和 PC5 上获得 PWM(但后两者仅当您正在使用具有这些引脚的 VQFN 封装)。

您还可以尝试使用 TCB 或 TCD 外设,它们也可以生成波形;使用适当的代码,这可能允许您在 PC0 或 PC1 上运行 PWM。

不可能将 PWM 放在任何其他引脚上。