arduino / ArduinoCore-avr

The Official Arduino AVR core
https://www.arduino.cc
1.24k stars 1.05k forks source link

How to add variant configurations after initializing the USB stack #260

Open XuGuohui opened 9 years ago

XuGuohui commented 9 years ago

Hi all, We're making the 3-party boards add-on package for Arduino 1.6.4. Our board need to be re-configured must after the USB stack being initialized, or our configuration will be overrided by the USB stack. Just like this:

int main(void)
{
    init();

    initVariant();

#if defined(USBCON)
    USBDevice.attach();
#endif

#if defined(BLEND_MICRO_8MHZ)
    // As the F_CPU = 8000000UL, the USB core make the PLLCSR = 0x02
    // But the external xtal is 16000000Hz, so correct it here.
    PLLCSR |= 0x10;             // Need 16 MHz xtal
    while (!(PLLCSR & (1<<PLOCK)));     // wait for lock pll
#elif defined(BLEND_MICRO_16MHZ)
    // The CPU clock in bootloader is 8MHz, change to 16MHz for sketches to run (i.e. overclock running at 3.3v).
    CLKPR = 0x80;
    CLKPR = 0;
#endif

    setup();

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }

    return 0;
}

We don't hope that makers need to replace the main.cpp manually but just install our board by using the board manager. How could we achieve that? Now we can add our boards using the board manager successfully, but leave this issue unresolved.

Any ideas would be appreciated! Thanks in advance!

NicoHood commented 9 years ago

Do it in the setup() function? Or duplicate the Arduino core and provide it with the boards.

XuGuohui commented 9 years ago

Hi NicoHood,

It must be annoyed to maker to add this init code in the setup function every time he creates a new sketch, and some makers may forget to add this code. It is verbose to duplicate the Arduino core since we only make a modification in main.c. Even though we duplicate the core, how to merge the original Arduino core when install the boards using the board manager?

XuGuohui commented 9 years ago

I finally resolve the issue by adding our custom main.cpp under the sub folder "variants\", e.g., variants\BlendMicro-8MHz, the same location as pins_arduino.h. But we don't know if it is the official method to fix such issues. We need the official confirmation or have to suffer from long time test.

cmaglie commented 9 years ago

@XuGuohui

the PLL initialization in the Arduino Core seems correct:

void USBDevice_::attach()
{
[....]
#if F_CPU == 16000000UL
        PLLCSR = 0x12;                                          // Need 16 MHz xtal
#elif F_CPU == 8000000UL
        PLLCSR = 0x02;                                          // Need 8 MHz xtal
#endif
        while (!(PLLCSR & (1<<PLOCK)))          // wait for lock pll
                ;

may you explain why you need to set F_CPU to 8Mhz when the external XTAL is 16? It would seem more logical to set F_CPU to 16Mhz and let the core select the correct PLLCSR.

XuGuohui commented 9 years ago

@cmaglie

First of all, when we design the hardware, we thought some users may hope to run their application at 16 MHz, which called over clock, so we have to solder the 16 MHz external crystal on the board. But most of time the CPU should run at 8 MHz to be stable. Therefore, we designed the bootloader to run at 8 MHz. If user want to run their application at 8 MHz, then the F_CPU is set to 8000000UL, then the USB stack select the PLLCSR as 0x02, treating the external crystal as 8 MHz, but actually the external crystal is 16 MHz. So we have to correct it after the USB stack initialization, i.e.

if defined(BLEND_MICRO_8MHZ)

// As the F_CPU = 8000000UL, the USB core make the PLLCSR = 0x02
// But the external xtal is 16000000Hz, so correct it here.
PLLCSR |= 0x10;             // Need 16 MHz xtal
while (!(PLLCSR & (1<<PLOCK)));     // wait for lock pll

If user want to run their application at 16 MHz, then the F_CPU is set to 16000000UL, then the USB stack choose the right PLLCSR configuration. But our bootloader runs at 8 MHz, so we only need to make a division of the clock, i.e.

elif defined(BLEND_MICRO_16MHZ)

// The CPU clock in bootloader is 8MHz, change to 16MHz for sketches to run (i.e. overclock running at 3.3v).
CLKPR = 0x80;
CLKPR = 0;

endif

That why we need to add some custom code after the USB stack initialization.

cmaglie commented 9 years ago

I see, F_CPU may have a different value from the external crystal, usually the prescaler is set to a 1:1 factor so this is not a issue, but your board indeed have an unusual setup to allow over-clocking.

What about changing the USBDevice::attach() to something like:

// Default F_XOSC == F_CPU unless otherwise specified
#ifndef F_XOSC
#define F_XOSC F_CPU
#endif

#if F_XOSC == 16000000UL
        PLLCSR = 0x12;                                          // Need 16 MHz xtal
#elif F_XOSC == 8000000UL
        PLLCSR = 0x02;                                          // Need 8 MHz xtal
#endif
        while (!(PLLCSR & (1<<PLOCK)))          // wait for lock pll

this way you can set F_XOSC via extra_flags in boards.txt:

myboard.build.extra_flags={build.usb_flags} -DF_XOSC=16000000L

and all the other existing boards can continue to work as usual.

For the CLKPR part I guess that there are no problems if you do the initialization before the USB Stack, right?

XuGuohui commented 9 years ago

@cmaglie

First I remove our main.cpp under the variants folder and then add the code in the method USBDevice::attach() as you mentioned above. Now the code looks like this:

    _usbConfiguration = 0; 
    UHWCON = 0x01;                      // power internal reg
    USBCON = (1<<USBE)|(1<<FRZCLK);     // clock frozen, usb enabled

    // Default F_XOSC == F_CPU unless otherwise specified
#ifndef F_XOSC
#define F_XOSC F_CPU
#endif

#if F_XOSC == 16000000UL
    PLLCSR = 0x12;                                          // Need 16 MHz xtal
#elif F_XOSC == 8000000UL
    PLLCSR = 0x02;                                          // Need 8 MHz xtal
#endif

//#if F_CPU == 16000000UL
//  PLLCSR = 0x12;                      // Need 16 MHz xtal
//#elif F_CPU == 8000000UL
//  PLLCSR = 0x02;                      // Need 8 MHz xtal
//#endif
    while (!(PLLCSR & (1<<PLOCK)));     // wait for lock pll 

Then I add blendmicro8.build.extra_flags={build.usb_flags} -DF_XOSC=16000000UL in the boards.txt. But it didn't compile the PLLCSR = 0x12 at all. It seams that the F_XOSC flag didn't set.

aweatherguy commented 7 years ago

I'd like to second the suggestion by cmagile on June 5, 2015. I use this modification with a 32U4 running on 3.3V with s a 16MHz clock input. F_CPU is divided down to 8MHz in the system clock prescaler, but the USB prescaler receives a 16MHz clock.

That code is now in the function USB_ClockEnable(). The clock input to the PLL prescaler does not have a consistent name on Atmel data sheets. On ATmega32U4 it is "PllPresc", on 8U2 and 16U2 it is "XTAL" and on AT90USB1286 it is "Source clock". Therefore a good name for the macro defining this frequency is not obvious. In each case however, it is the output of the main clock multiplexer, and is always applied to the PLL prescaler.

I think that "F_XOSC" would be okay or "F_XTAL" or "F_PLL_PRESCALER". The name isn't all that important however....I would jus tlike to see this change implemented in USBCore.cpp.

Please?


As an aside, the question has come up whether Atmel approves of running on 3.3V with an input clock of 16MHz. Clearly this is too fast for the entire MCU and the system clock prescaler must be used to reduce it to 8MHz or less.

It is less clear whether this is okay for the USB interface. I have not been able to get an answer from Atmel on this, but here is some evidence from the 32U4 data sheet that it might be okay.

First in section 6.9.2 regarding the system clock prescaler register:

The CKDIV8 Fuse determines the initial value of the CLKPS bits. If CKDIV8 is unprogrammed, the CLKPS bits will be reset to “0000”. If CKDIV8 is programmed, CLKPS bits are reset to “0011”, giving a division factor of 8 at start up. This feature should be used if the selected clock source has a higher frequency than the maximum frequency of the device at the present operating conditions. Note that any value can be written to the CLKPS bits regardless of the CKDIV8 Fuse setting. The Application software must ensure that a sufficient division factor is chosen if the selected clock source has a higher frequency than the maximum frequency of the device at the present operating conditions. The device is shipped with the CKDIV8 Fuse programmed.

Then in section 6.3 there is footnote 3 for table 6-3:

  1. If 8 MHz frequency exceeds the specification of the device (depends on VCC), the CKDIV8 Fuse can be programmed in order to divide the internal frequency by 8. It be ensured that the resulting divided clock meets the frequency specification of the device.