Arduino Zero SAMD21F18 - Clock Tree, SERCOM, UART minimum baud rate? - Page 1
Forum section: electronics / general

author: Photon939
author_rank: Regular Contributor
timestamp: January 14, 2021, 04:19:39 am
content: I am hoping someone can help me understand the SAMD21F18's internal clock peripherals a bit better - I am still very much a noob taking first steps away from the 8 bit AVR Arduino universe. I came across the Arduino Zero-ish M0 boards on ebay and so far they work great and I have been able to do enough googling to get the extra SERCOM peripherals working for additional UARTs.I am currently having a problem in that the UART peripherals do not allow baud rates low enough for my friend's project interfacing to an LG minisplit heat pump digital thermostat bus. The data bus is a half-duplex 100 baud TTL serial stream running at 12v logic levels.From what I have been able to glean from google searches and trying to read some of the datasheet before my eyes glaze over is that the peripherals have their own clock sources that are independent from the CPU clock. (Generic clock generators)If that is the case, would it be possible to enable a clock divider on the SERCOM devices to allow for lower baud rate outputs?I previously was able to get a UART working at 100 baud on an Arduino nano by running the cpu clock with a divider of 4 since the clock affected everything. Now I would like to be able to port that code to the SAMD21.Thanks for your time.

author: ataradov
author_rank: Super Contributor
timestamp: January 14, 2021, 04:47:11 am
content: Yes, it is possible. Each peripheral is clocked by a GCLK. Typically you just clock every peripheral from GCLK, which also clocks the core.But you can use another GCLK to clock the SERCOM, and each GCLK has a divider.How to do that in Arduino ecosystem - I have no clue. I'd start with searching for "GCLK". At some point you will see a code like this:Code:PM->APBCMASK.reg |= PM_APBCMASK_SERCOM3;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM3_GCLK_ID_CORE) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);SERCOM3->USART.CTRLA.reg =........;GCLK_CLKCTRL_GEN(0) selects GCLK.Enabling another GCLK would look like this:Code:// Divide the clock by 4 on the GCLKGCLK->GENDIV.reg = GCLK_GENDIV_ID(3) | GCLK_GENDIV_DIV(4);// Clock GCLK from the DFLL48 MHzGCLK->GENCTRL.reg = GCLK_GENCTRL_ID(3) | GCLK_GENCTRL_SRC(GCLK_SOURCE_DFLL48M) |GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);

author: jackthomson43
author_rank: Contributor
timestamp: January 14, 2021, 05:47:02 pm
content: Quote from: Photon939 on January 14, 2021, 04:19:39 amNopes it won't be possible withArduino Zero.

author: ataradov
author_rank: Super Contributor
timestamp: January 14, 2021, 05:52:51 pm
content: Quote from: jackthomson43 on January 14, 2021, 05:47:02 pmWhat is the point of this answer if I already described that it is possible and how to do that?

author: cv007
author_rank: Frequent Contributor
timestamp: January 14, 2021, 11:00:55 pm
content: I have a samd10, not an expert. Also do not use Arduino. But the following may be useful. Or not.SYSCTRL = various clock sources, enable/setup hereGCLK = clock generators created from SYSCTRL choices (some number of generators available, maybe 9 for samd21)GCLK = peripheral generator selection and divisionSYSCTRL - OSC8M on by default, and has a PRESC set for 1MHz output (probably only clock with PRESC/division option at this level)GLCK - generator ID 0 is enabled by default using OSC8M, can be changed, can be divided again hereCreate clock generators IDn in GCLK, and assign them to peripherals, with a clock division factor if needed (is divided at the generator).SERCOM - You need to be able to fit the baud value in 13bits if using fractional mode, or 16bits if arithmetic mode. Fractional mode is most likely the option wanted in most cases. Your source is selected in GCLK from among the generators already setup.If you are using Arduino, looking at their github page-https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/startup.cThe main clock is 48MHz on GCLK ID 0, too fast for 100 baud (GCLK ID 0 is exclusively used by Arduino sercom code)They move the OSC8M to GCLK ID 3 without the OSC8M prescale, so is 8MHz. Could be used.https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/SERCOM.cppwhere they init the SERCOM clock, will need to modify to allow another clock source-Code:...void SERCOM::initClockNVIC( void )...//Setting clockGCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is sourceGCLK_CLKCTRL_CLKEN ;...Since GCLK0 is too fast, change so it selects GCLK3 (8MHz) when needed, so modify (also change declaration in sercom.h to match)-Code:void SERCOM::initClockNVIC( bool slow = false ) //default is not slow (GCLK ID 0, 48MHz)...//Setting clockauto gclk = slow ? GCLK_CLKCTRL_GEN_GCLK3 : GCLK_CLKCTRL_GEN_GCLK0; //8MHz or 48MHzGCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)gclk | // Generic Clock Generator 0 or 3 is sourceGCLK_CLKCTRL_CLKEN ;...Now in the sercom init, modify to enable the slow clock source-Code:void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate){auto slow = baudrate < 600; //slow = true if baudrate < 600if( slow ) sampleRate = SAMPLE_RATE_x16; //need x16 for slowinitClockNVIC( slow );resetUART();//Setting the CTRLA registersercom->USART.CTRLA.reg =SERCOM_USART_CTRLA_MODE(mode) |SERCOM_USART_CTRLA_SAMPR(sampleRate);//Setting the Interrupt registersercom->USART.INTENSET.reg =SERCOM_USART_INTENSET_RXC | //Received completeSERCOM_USART_INTENSET_ERROR; //All others errorsif ( mode == UART_INT_CLOCK ){uint16_t sampleRateValue;if (sampleRate == SAMPLE_RATE_x16) {sampleRateValue = 16;} else {sampleRateValue = 8;}// Asynchronous fractional mode (Table 24-2 in datasheet)// BAUD = fref / (sampleRateValue * fbaud)// (multiply by 8, to calculate fractional piece)auto serclock = slow ? 8000000ul : SystemCoreClock; //8MHz or 48MHzuint32_t baudTimes8 = (serclock * 8 ) / (sampleRateValue * baudrate);sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8 );sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8 );}}So, now when baudrate is 100-GCLK ID 3 is used, which is 8MHzbaudtimes8 = 64000000/1600 = 40000BAUD = 40000/8 = 5000 (13bits - ok)FP = 0