SAMD10: What's the best way to set up a timer to trigger every 330ns (3MHz) ?? - Page 1
Forum section: electronics / general

author: Refrigerator
author_rank: Super Contributor
timestamp: November 30, 2020, 09:02:52 pm
content: I've been looking at ASF4 along with code generated by Start.atmel and a bare metal code example (courtesy of Alex) with a timer implementation but can't seem to figure out what's the best way to set up a timer to trigger a function every ~330ns (3MHz).For starters i can't even figure out how to set up a timer on bare metal. I've chosen bare metal over ASF4/Startbecause ASF4 has proven to be buggy.(or i suck at coding)Also it seems to be impossible to set timer period lower than 1µs in Start.atmel.The purpose of this timer will be to time the signal for a WS2812-2020 LED.I've figured out that (imo) the best way to transmit each of the data bits for the LED's would be to place the color data bits between a 1 and 0.For example a 1 becomes a 110 and a 0 becomes a 100 and if i shift a bit out a pin every 330ns the timings should be correct to operate the WS2812-2020 LED's.I've never worked with timers this fast/close to CPU clock. Most of the time it has been only some 100-1000ms timed events.CPU is set to run at 48MHz, so if i divide it by 16 i should get 3MHz.

author: PCB.Wiz
author_rank: Super Contributor
timestamp: November 30, 2020, 10:53:33 pm
content: Usually such tight timing isnotdone using interrupts, as you need to disable other interrupts anyway, to preserve the very tight limits.It is more common to use some HW to assist (as you say by collecting bits into a SPI or UART stream), or by running in-line code with interrupts off.

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2020, 11:20:06 pm
content: There is no way D10 can execute a function at 3 MHz rate. Interrupt entry and exit are 15 clock cycles. So you interrupt handler that does not do any work at all can't run faster than 1 MHz.And then running at 48 MHz you have one wait state, so your actual execution speed is slower.Also, even in ideal conditions, your function that is executed at 3 MHz rate can't be longer than 16 instructions. And that would be 100% CPU utilization. No need for a timer, just hard code those 16 instruction in a loop.So yes, you need to figure out a way to use hardware assistance from a suitable peripheral.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 30, 2020, 11:58:45 pm
content: Quote from: ataradov on November 30, 2020, 11:20:06 pmThat explains why i couldn't get my timer period below ~800ns.Must be some trick that's done in arduino nano to work because it's only 16MHz and it works perfectly fine, i'll have to investigate.And i thought making the LED's work was the easy part.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 12:03:10 am
content: I'm guessing it is using SPI or something like that to help it. I'm not too familiar with the way those LEDs are controlled.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 12:09:02 am
content: Looks like i can perhaps use the TCC to generate PWM, which is basically what the LED signal is.0 = ~33%, 1 = ~66% duty cycle.Quote

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 12:11:50 am
content: It also looks like TCC can be interfaced through DMA, so in theory i could use TCC as trigger for my DMA transfer and dump PWM values into TCC with every cycle, no?This way i would no longer need any work from the CPU and the whole thing should work like clockwork.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 12:13:19 am
content: But if you need to change the bit width every cycle, then PWM may not be the best option. It will still help, of course, especially if you use buffer register to update the duty cycle.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 12:14:50 am
content: Quote from: Refrigerator on December 01, 2020, 12:11:50 amYes, but you would need a continuous chunk of memory to prepare the data for the PWM. You would need at least a byte for each bit you want to send. This can get costly.Some combination of TCC and DMA with linked lists would probably work, but it is not as easy as simply pointing DMA at TCC. Some thinking and trying would be required.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 12:17:08 am
content: Also can serial wire (SWDIO) be used as a general serial interface or is it system-reserved?Would be kind of unfortunate if yes, because on my previous board revision i had it connected to LED data, but changed it over to another pin so that i could debug the system without the LED's interfering and vice versa.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 12:21:58 am
content: SWD interface itself can't be used for anything else. It is not a general serial interface, it is very specific to the core. Pin allocated for SWDIO can be used as a normal GPIO pin, but you won't be able to debug, of course.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 12:22:54 am
content: Quote from: ataradov on December 01, 2020, 12:14:50 amYes, i completely failed to take into consideration that DMA transfers are no less than 8 bit.I have 6 LED's in series and they all would need 432 bits but with DMA that would be multiplied by 8 and would result in 3456 bits (!!!!) so that's basically all of my RAM.Fortunately my MCU won't be doing anything else besides LED control (because everything else is DMA) so perhaps i can just send the bits full auto from my main loop.Ps: i'll draw a diagram to explain what my MCU will be doing, but now it's 2:30AM and i'm feeling sleepy.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 12:32:11 am
content: 432 bits of the serial data would be 432 bytes. You probably can afford that.But you can also do a combination solution. Have 2 DMA buffers that alternate. One is used by the DMA and the other one is filled by the application. The buffer can be 72 bytes, for example.

author: cv007
author_rank: Frequent Contributor
timestamp: December 01, 2020, 03:18:22 am
content: Use the sercom spi.bit time 250ns - 4MHz spiconvert bits to nibbles-0 -> 1000 - 250ns H 750ns L1 -> 1110 - 750ns H 250ns La 00 means 10001000 gets sent out spi (DORD=0)01 1000111010 1110100011 111011101us per spi byte, which does give a little time to be calculating the next byte although you possibly still may want to just poll the irq instead of letting the isr take up time. With only a single tx buffer, dma would work better but without you still have close to 2us before the spi runs dry and may work unless you have other irq's going that could take too much time.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 08:49:52 am
content: Quote from: cv007 on December 01, 2020, 03:18:22 amAll of my SERCOM units are taken already.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 09:06:05 am
content: Here's a crude diagram of what will be happening in my MCU, there's no LED data transmission in there yet.F1() is the function that calculates TX data and LED data. SERCOM unit work in UART mode at 115200 baud.SERCOM's are labeled as _x _y _z because they are set up when the MCU is started and can be RX+TX+TX in any configuration.Edit: this is the thing running at max speed.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 10:47:44 am
content: A single bit of the LED data is about 1µs so 24 bits would take 24µs to transmit, add to that the >280µs low time for reset and you get ~304µs(min) per transaction.UARTS will be communicating at 115200 baud, 40 bits at a time so that would take ~347µs.This leaves me with 43µs of free time.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 10:52:59 am
content: Quote from: ataradov on December 01, 2020, 12:32:11 amOh i mistook bit for byte, i thought i had 4kbitof RAM.If i have 4kbytethen it's all good, since my MCU won't be doing much other than data routing through DMA and minimal processing.

author: MIS42N
author_rank: Frequent Contributor
timestamp: December 01, 2020, 12:22:31 pm
content: A somewhat left field idea. I am not familiar with this device, but it says the base BAUD clock for the USART is 3MHz (1/16 of 48MHz). A 0 code would be 100 and a 1 code 110. Always starts with a 1 and ends with a 0. If the USART was configured for asynchronous 7 bits it outputs 9 bits, starting with a 0 and ending with a 1. Using an inverter this can be 1 and 0, with 7 data bits in between. This would allow 3 codes to be transmitted at a time, 8 combinations to look up in a lookup table, and serviced every 3uS. USARTs are usually buffered, so may get away with 5uS and test if it will take one or two bytes on each entry.If your application allows, blocking interrupts and servicing via timing loops is simpler.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 03:20:37 pm
content: Quote from: MIS42N on December 01, 2020, 12:22:31 pmUnfortunately all of my UART's are taken already.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 05:53:20 pm
content: Set up PWM in Start.atmel and tested it out.The PWM function settings are weird because they do not match what i set and the HAL drivers are quicky.For starters this function from HAL drivers:Code:int32_t pwm_set_parameters(struct pwm_descriptor *const descr, const pwm_period_t period, const pwm_period_t duty_cycle){ASSERT(descr);_pwm_set_param(&descr->device, period, duty_cycle);return ERR_NONE;}I set it up in my main as:Code:pwm_set_parameters(&PWM_0, 100, 10);That would mean 100µs period and 10% PWM right? Nope.What i get is ~25µs and ~99% duty cycle. Now it's most likely is that i have the inverted duty cycle value, but why 99%?Changing code to:Code:pwm_set_parameters(&PWM_0, 10, 10);Gets me a duty cycle of 90% and a period of ~2.5µs. Now that's more like it. And just like that i have 250ns pulses and that's at 8MHz clock.Now i just need to find a way to change the pulse width every period and to set the period to 1µs.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 06:05:11 pm
content: You are making a mistake of using ASF again. TCC initialization is very simple (again, the code for D21, so change the constants and buses as appropriate for D10):Code:HAL_GPIO_PWM_pmuxen(PORT_PMUX_PMUXE_F_Val);PM->APBCMASK.reg |= PM_APBCMASK_TCC0;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(26/*TCC0_GCLK_ID & TCC1_GCLK_ID*/) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC;TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;TCC0->PER.reg = 5555;TCC0->CC.reg = 1111;TCC0->CTRLA.reg |= TCC_CTRLA_ENABLE;After that PWM will start working and you need to setup DMA to write into CCB register. This is a buffer register and the value will transfer to the CC[ x ] on the next update cycle.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 06:05:26 pm
content: Ok so the weirdness continues.In the functionpwm_set_parameters(&PWM_0, 4, 100);the PWM value does nothing, no matter what i do it's ~250ns.........actually that perfectly matches what i set in Start.AtmelThere has to be some hidden setting to get what i want, and if i can get DMA to dump a byte in there every period then i'll be set.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 01, 2020, 06:07:32 pm
content: Quote from: ataradov on December 01, 2020, 06:05:11 pmThis was just an experiment to find out if i can get PWM to generate short enough pulses

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2020, 06:08:17 pm
content: PWM can generate pulses as short as 1 input clock cycle. The issue with ASF is that it tries to operate in real-world units (us, %), which is not what you want in most cases where you want fine control over the values. The conversion errors will create unpredictable results.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 02, 2020, 03:55:47 pm
content: So far i've been unsuccesful trying to get PWM to work the way i want it to.The problem is my lack of coding skillNow my code consists of a mix of ASF4 and bare metal.I've also gained the power to summon hard faults on demand with code that checks out with 0 errors or warnings.ASF4 isn't horribly bad it's just that there's soooo many layers to this onion.Anyways i'll keep trying to get something, the goal is to set TC2 PWM output on PA10 with 1µs period.Edit: through DMA of course.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 02, 2020, 04:26:34 pm
content: After digging through i found the way to write to TC2 CC registers:TC2->COUNT32.CC.reg. It may not the the correct way but it's something.I got it from this line here inhri_tc_d10.h:Code:static inline void hri_tccount32_write_CC_reg(const void *const hw, uint8_t index, hri_tccount32_cc_reg_t data){TC_CRITICAL_SECTION_ENTER();((Tc *)hw)->COUNT32.CC.reg = data;TC_CRITICAL_SECTION_LEAVE();}Writing to CC changes the period, but weirdly enough, writing to CC does nothing, when it's supposed to change the PWM ( at least according to ASF4).Also i just noticed that this is not my first time setting PWM like this. I've previously done something similar with STM32:Code:#define Set_PWMhtim22.Instance->CCMR1Edit: i'm still working on the ASF4 generated template because i can't set up TC2 on bare metal yet. Could it be that my initial settings in Start.Atmel are incorrect?

author: Refrigerator
author_rank: Super Contributor
timestamp: December 02, 2020, 04:41:27 pm
content: AlsoTC2->COUNT32.CCB.regdoes not work and gives an error.

author: ataradov
author_rank: Super Contributor
timestamp: December 02, 2020, 05:07:01 pm
content: Quote from: Refrigerator on December 02, 2020, 04:41:27 pmTCs don't have buffer registers, only TCCs do. And only TTCs can be used for this application.How CC[ x] and PER registers affect the output depends on the mode the timer is configured for.You need to read the datasheet to understand how all that works, you won't get far by poking random values into random registers.

author: ataradov
author_rank: Super Contributor
timestamp: December 02, 2020, 05:09:03 pm
content: Quote from: Refrigerator on December 02, 2020, 03:55:47 pmHFs are run-time errors, they can't be detected at compile-time, so compiler warnings are irrelevant here.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 02, 2020, 09:02:46 pm
content: I've looked through the datasheet but i'm still struggling with TCC0, i guess i'm missing some details still.As far as i can understand this piece of code right here:Code:void TCC_setup(void){//HAL_GPIO_PWM_pmuxen(PORT_PMUX_PMUXE_F_Val);// function F = WO// LED_data out on PA10// PA10 = WOPM->APBCMASK.reg |= PM_APBCMASK_TCC0;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TCC0_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC;TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;TCC0->PER.reg = 7; // at 8mhz makes period 1mhzTCC0->CC.reg = 2; // will generate ~375 µs pulseTCC0->CTRLA.reg |= TCC_CTRLA_ENABLE;}Should work as long as i enable my PA10 as an output in F mode.But i'm currently struggling with pin enabling, seems every library has it's own way of setting the pinmux etc.So i'm still looking through the datasheet to see how to do it in bare metal.Also i can't even get TCC0 PWM to work with Start.Atmel just to test it.TC1 and TC2 work fine.

author: ataradov
author_rank: Super Contributor
timestamp: December 02, 2020, 09:07:21 pm
content: WO corresponds to CC. So you need to set it instead of CC as the code does.HAL_GPIO_PWM_pmuxen() is the function that does it in bare-metal. It comes from herehttps://github.com/ataradov/mcu-starter-projects/blob/master/samd10/hal_gpio.h

author: Refrigerator
author_rank: Super Contributor
timestamp: December 02, 2020, 09:49:36 pm
content: Quote from: ataradov on December 02, 2020, 09:07:21 pmI completely missed that one, now it's all working

author: Refrigerator
author_rank: Super Contributor
timestamp: December 02, 2020, 11:48:15 pm
content: I thought i was in the clear to go and set up DMA but looks like that's not the case.Writing toTCC0->CC.regworks fine but writing toTCC0->CCB.regdoes nothing, same with TCC0->CCB.reg.At least i can finally make my LED's glow white.Edit: watch window shows that the value i write does appear inTCC0->CCB.regbut pulse width does not change

author: ataradov
author_rank: Super Contributor
timestamp: December 02, 2020, 11:50:27 pm
content: You need to enable buffered mode. Again, read the datasheet.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 03:23:14 pm
content: Try as i might i can not get CCB to transfer over to CC.I've looked through the datasheet and the registers:QuoteQuoteMy CTRLBSET.LUPD is all 0 and i can set LUPD to 1 withTCC0->CTRLBSET.reg |= 0b10;and then reset it again withTCC0->CTRLBCLR.reg |= 0b10;, i can see the changes in watch window.My LUPD is 0 so double buffering is enabled but the value from CCB still does not transfer to CC.Am i missing something? TCC0 works if i set CC manually.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 04:09:05 pm
content: Writing to:Code:TCC0->PERB.reg = 8;// set periodSets STATUS register to 129, which is 10000001, meaning STATUS.PERBV register is 1.But writing to:Code:TCC0->CCB.reg = 6;// set counter compareDoes not change the STATUS.CCBV2 register to 1, meaning no transfer. But why?After the TCC0 is enabled STATUS register returns to 0, meaning that transfer from PERBV to PERB was sucessful, yet CCB is completely ignored.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 04:16:44 pm
content: Writing to:Code:TCC0->CCB.reg = 2;Before TCC is enabled or after TCC is enabled makes no difference toSTATUS.CCBV

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 06:25:45 pm
content: STATUS.CCBV is R/W but when writing to it like this:Code:TCC0->STATUS.reg |= (uint32_t)0b1 << 18;// CCVBNothing happensEdit: Writing 1 to STATUS.CCBV clears itQuote

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 06:32:55 pm
content: Looking at CCBV bits while TCC is running is not very useful, since the bit is cleared on each cycle. You need to be looking at the actual TCC output or the corresponding CC values. The value may have been transferred by the time you are looking at it.But PERB would also have been transferred to PER, so this is strange.I don't have time to experiment right now, I'll try it later.Also, there are SET and CLR set of registers specifically so that you would not need to to read-modify-write operation, so you need to use "TCC0->CTRLBSET.reg = 0b10;", not "TCC0->CTRLBSET.reg |= 0b10;"Ah, I see, you are trying to write them while TCC is disabled, I have no idea how it will behave in this case. Butffer registers must be written when TCC is enabled and running.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 06:35:55 pm
content: Quote from: ataradov on December 03, 2020, 06:32:55 pmThe values do not transfer, also i tried both before and after enabling TCC0 and nothing happens.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 06:38:45 pm
content: Does the actual output of the timer change? On the pin?Reading back registers may not work due to synchronization or something like that.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 06:44:38 pm
content: Quote from: ataradov on December 03, 2020, 06:32:55 pmWriting to TCC0->PERB (and PER) worked both when TCC was disabled and enabled, same with TCC->CC, which updated both when TCC0 was disabled and enabled.But writing to TCC0->CCB only updates the CCB register, without ever enabling STATUS.CCBV or transfering the value over to CC no matter whether the TCC0 is enabled or not.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 06:47:13 pm
content: Quote from: ataradov on December 03, 2020, 06:38:45 pmOnly writing directly to CC works.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 06:52:17 pm
content: CCB can't be transferred to CC when the timer is not running. There can't be an UPDATE even if the timer is not running. Obviously writing the registers directly works regardless.Also, what is your period at this time? If the period is not set, then there won't be an UPDATE event. For the NPWM mode UPDATE happens when the COUNT==PER. And if both COUNT and PER == 0, then there won't be an UPDATE event.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 07:06:47 pm
content: There has to be a catch somwhere.According to:QuoteClearly it does not.Double buffering is enabled as evidenced byTCC0->PERB.reg = 8;working and settingSTATUS.PERBVregister to 1. This is further confirmed by CTRLBCLR.LUPD register being 0.The only clue i could find so far is in this sentence here:QuoteAlso the way this sentence is worded is hard to read for me so i'll try to break it down::IF writing a one to the Lock Update bit in the Control B Clear register (CTRLBCLR.LUPD)and the PER and CCx are used for a compare operation:THEN the Buffer Valid bit is set when data has been written to abuffer register.But CC stands for capture compare, how would PWM work if PER and CCx aren't used for compare op?Quote from: ataradov on December 03, 2020, 06:52:17 pmPER is set to 8 and at 8MHz that gives me a pretty good waveform.Also writing to CCB even when running does nothing and i'll get a screenshot of the watch window that will show just how BS this whole situation is.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 07:29:50 pm
content: Ok, I don't know what you are doing wrong. Here is the code that was tested on D10 Xmini:Code://-----------------------------------------------------------------------------void tcc_init(void){HAL_GPIO_PWM_pmuxen(PORT_PMUX_PMUXE_F_Val);PM->APBCMASK.reg |= PM_APBCMASK_TCC0;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TCC0_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC;TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;TCC0->PER.reg = 100;TCC0->CC.reg = 50;TCC0->CTRLA.reg |= TCC_CTRLA_ENABLE;}//-----------------------------------------------------------------------------void tcc_test(void){// TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD;TCC0->PERB.reg = 50;TCC0->CCB.reg = 10;// TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD;}Initially frequency is higher and duty cycle is 50%. After I call tcc_test() from a button press, Frequency doubles and duty cycle changes to 20%.The idea behind LUPD is to let you atomically update multiple values and have them applied at the same time. In the code LUPD access is commented out because you don't need it for things to work, but you need it if it was actual production code.If you only update one register (as would be the case in your real application), then you don't need to worry about LUPD.The code was tested observing the actual output. I don't know what happens to the registers, I don't have time to run the debugger.Watching register is not a good indication of what actually happens, it may be IDE caching the values or something like this. Observe the actual PWM output on the pin.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 07:33:28 pm
content: I took screencaps of the code and the watch window step by step. if some of them appear to be at the same spot that's because i pressed break all after running the code to capture the watch window.And it shows that PERB works and transfers to PER.But not CCB to CC.step6debug1_the_bullshit.JPG sums it up basically.CCB is 2CC is 6CCBV is 0CCB was set to 2 in the EXTIRQ that's tied to the button while the MCU was running and i pressed the buttontwicefor good measure. Yet CCBV remains 0 even though CCB and CC do not match.This MCU continues to be the bane of my existence.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 07:37:37 pm
content: Ok, I have no idea what you are doing, Once more time. It is not correct to write buffer values while timer is disabled. I have no idea what would happen if you do. One of them may transfer and the other may not. It is literally not predictable.Also, debugging this step by step also makes no sense. All of this is very timing sensitive.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 07:39:59 pm
content: Here is a complete project I'm running. Observe PA10 before and after you press the button.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 09:09:59 pm
content: I made a new project and adapted a piece of your example code to work and everything is fine now.Since i don't know how to properly code in bare metal i rely heavily on Start.Atmel and in this case seems like it screwed something up big time.Now i generated the same project but instead of setting up PWM on TCC0 i set up PA10 as function F and used your bare metal example function to setup TCC0 and got everything to work.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 10:55:51 pm
content: Well i thought i was in the clear but when i set DMA to send a single byte to CCB it didn't work and entered the dreaded dummy handler.Even writing to CCB when DMA is in SW only trigger does the same.But when i change the address to some dummy variable it works fine so writing to CCB through DMA is my next hurdle it seems.Checked the datasheet and didn't immediately find any special conditions regarding DMA write to CCB registers, only this:QuoteAt this point i'm pretty sure it's ASF4 tripping me over once again. But anyways i'll keep experimenting to see what's going on exactly.Obviously writing to the register through DMA generates a fault. But i can still check if i get a DMA trigger from TCC0 by just changing the destination of my data.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 10:58:26 pm
content: Try writing a word first to see if this is somehow register not wanting to accept the byte write.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 11:00:21 pm
content: Yep if i set DMA destination to some dummy variable the value gets changed and the set amount of data gets transmitted.Also i enable DMA without triggering so it can only be triggered by TCC0, this time it's set to trigger on overflow.So the problem lies (in me using ASF4ahem) in DMA trying to transfer to CCB but erroring out.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 11:01:41 pm
content: What happens if you use word transfers instead of byte transfers?

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 11:03:58 pm
content: Quote from: ataradov on December 03, 2020, 11:01:41 pmI had to google how long a word is (16bytesbits according to google).I'll go check right now.Edit: in watch CCB shows up as auint32_t

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 11:06:48 pm
content: Word is 32 bits.It is literally the setting for the BEATSIZE:0x0 BYTE 8-bit access0x1 HWORD 16-bit access0x2 WORD 32-bit accessYou need to access the register with its native size.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 11:37:02 pm
content: Setting beat size to 32 bytes makes DMA callback trigger but the value does not appear in CC, perhaps my destination address is wrong?Edit: LED_data is a single uint32_t variable but transfer size is set to 1 so it should be ok.Code:_dma_set_source_address(0, (void *)LED_data);_dma_set_destination_address(0, (void *)TCC0->CCB.reg);

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 11:50:18 pm
content: (void *)&TCC0->CCB.reg

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 11:53:38 pm
content: Quote from: ataradov on December 03, 2020, 11:50:18 pmAnd that's why i fear bare metal coding because i miss obvious details like these.Also would casting the uint8_t as uint32_t work? i don't want to quadruple my RAM usage.

author: ataradov
author_rank: Super Contributor
timestamp: December 03, 2020, 11:54:48 pm
content: You missed that in ASF code. This has noting to do with bare-metal or not.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 03, 2020, 11:56:52 pm
content: Quote from: ataradov on December 03, 2020, 11:54:48 pmWhat i meant was that i miss details (in general) and bare metal coding is a pretty detailed process (imo).

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 12:50:47 am
content: Noticed that not all of the 32 bits were transfering and found out inTable 29-6. Register Summarythat the register is 24 bits, which is fine.I haven't yet found a way to save space, because now every LED data bit becomes a 32-bit word.Edit: actually if i made one DMA transfer from uint8_t to a uint32_t and another to transfer from that to CCB then maybe.

author: ataradov
author_rank: Super Contributor
timestamp: December 04, 2020, 12:52:53 am
content: So if you use the correct register address, but do a byte transfer, it does not work?Does it do anything if you do a manual write:Code:*((uint8_t *)&TCC0->CCB.reg) = 123;Or just CC direcly?Code:*((uint8_t *)&TCC0->CC.reg) = 123;If this manual code does not work, then there is not much you can do, those registers expect a word write.Chaining DMA transfers like this would not work most likely. It will be a nightmare to trigger all of this.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 01:04:51 am
content: 8-bit transfer works and multiple 8-bit transfers work alsoI think i've cleared enough hurdles to finally be able to continue on with my program (knock on wood).

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 02:00:15 am
content: Was about to go sleep because it's almost 4AM but couldn't go without making sure i can send my data out.And guess what it finally works.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 02:23:42 am
content: LED's work but they're glitchy sometimes.First LED always sets the wrong color, which would mean bad timings most likely because these LED's reshape and correct the timings on DO, hence why the rest work fine.I would upload a pic but it's my fourth time trying to write this message, i hope not including the pic will make this message appear instead of redirecting to "start new topic".Just needed a downsize.

author: ataradov
author_rank: Super Contributor
timestamp: December 04, 2020, 02:27:10 am
content: I would suggest driving TCC with higher clock so that your compare and period values are not so low and you have finer control.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 02:32:20 am
content: Quote from: ataradov on December 04, 2020, 02:27:10 amI'm only running at 8MHz right now and i plan to crank my clocks all the way up to 48MHz to make calculations quicker and to give me better timings also.But i think it's amazing that i can repurpose hardware peripherals to generate custom serial signals that would otherwise be impossible to achieve any other way.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 05:58:05 pm
content: Found the reason to the glitchyness, turns out DMA wasn't going fast enough and would skip a beat or two sometimes.Increasing PER to 9 fixed the glitchyness.Also seems like at 8MHz with PER = 9, CC = 3 and CC = 6 produce perfect timings. According to WS2812-2020 datasheet the timings are barely correct but according to neopixel library they are spot on, and i've confirmed that the neopixel library on arduino nano works well with these LED's.

author: Refrigerator
author_rank: Super Contributor
timestamp: December 04, 2020, 06:18:07 pm
content: Also the very first few bytes would be lost sometimes so i added a few dummy bytes in front and everything is fine now.I assume increasing the clock to 48MHz would fix most of these issues.