SAMD10: Having problems setting up UART with DMA in ASF4/START - Page 1
Forum section: electronics / general

author: Refrigerator
author_rank: Super Contributor
timestamp: November 17, 2020, 09:32:07 pm
content: For the past couple of days i've been reading through ASF4/START API reference manual along with project examples from the START.Atmel website to get myself familiar with how to configure the UART to work with DMA.Unfortunately i'm having a hard time getting the hang of it, so i've come here (once again) to ask for advice.My goal with this micro is much bigger than what i'm trying to do right now but for now what i want to do to get started is to setup SERCOM0 UART to transmit whatever it receives right after it receives it (exho sort of) by using DMA.What i'm trying to do it to make the UART place received data into an address through DMA and the take the same data from that same address with DMA and transmit it.Below are the contents of my main.c and i tried to write comments on every line and why i did what i did.Code:#include <atmel_start.h>#include <hpl_dma.h>#define Buffer_length 12// 12 bytes just to fit "hello World!"uint8_t RX_DATA_buffer;uint8_t TX_DATA_buffer;// currently unused/** DMA channel 0 transfer complete callback* This means all of RX data has been copied to RX_DATA_buffer[]*/void dmac_channel_RX_cplt_callback(struct _dma_resource *resource){/* Disable DMA CH-0 */_dma_enable_transaction(SERCOM0_DMAC_ID_RX, false);// turn off DMA from RX to prevent overwriting the data// actually this should disable itself after block transfer because of my setup in START.Atmel// placing break point on above line never halts so i assume it never reaches this point./* Enable DMA CH-1 */_dma_enable_transaction(SERCOM0_DMAC_ID_TX, true);// turn on DMA to TX to transmit data}/** DMA channel 1 transfer complete callback* This means all of RX_DATA_buffer has been passed to TX*/void dmac_channel_TX_cplt_callback(struct _dma_resource *resource){/* Enable DMA CH-0 */_dma_enable_transaction(SERCOM0_DMAC_ID_RX, true);// turn on DMA from RX to save data to UART_RX_buffer[]/* Disable DMA CH-1 */_dma_enable_transaction(SERCOM0_DMAC_ID_TX, false);// disable DMA to TX to not transmit while receiving}/** Configure DMA channel 0*/void config_dma_channel_0(void){struct _dma_resource *dma_res;_dma_set_source_address(SERCOM0_DMAC_ID_RX, (void *)&(((Sercom *)(USART_0.device.hw))->USART.DATA.reg));// <-- the sercom address is where i see received data in watch window// so i assume it's correct_dma_set_destination_address(SERCOM0_DMAC_ID_RX, (void *)RX_DATA_buffer);// <-- this doesn't seem to work because nothing appears on watch/* Set DMA CH-0 block length */_dma_set_data_amount(SERCOM0_DMAC_ID_RX, (uint32_t)Buffer_length);/* Get DMA CH-0 resource to set the application callback */_dma_get_channel_resource(&dma_res, SERCOM0_DMAC_ID_RX);/* Set application callback to handle the DMA CH-0 transfer done */dma_res->dma_cb.transfer_done = dmac_channel_RX_cplt_callback;/* Enable DMA CH-0 transfer complete interrupt */_dma_set_irq_state(SERCOM0_DMAC_ID_RX, DMA_TRANSFER_COMPLETE_CB, true);}/** Configure DMA channel 0*/void config_dma_channel_1(void){struct _dma_resource *dma_res;/* Set DMA CH-1 source address (SRAM: adc_result)*//** the destination address is the same as source address because according to the SAMD10 datasheet when reading from this address i get the RX buffer* and when writing to this address it writes to the TX buffer* from datasheet:25.6.2.4 Data RegisterThe USART Transmit Data register (TxDATA) and USART Receive Data register(RxDATA) share the same I/Oaddress, referred to as the Data register (DATA). Writing the DATA register will update the Transmit Data register.Reading the DATA register will return the contents of the Receive Data register.*/_dma_set_destination_address(SERCOM0_DMAC_ID_TX, (void *)&(((Sercom *)(USART_0.device.hw))->USART.DATA.reg));_dma_set_source_address(SERCOM0_DMAC_ID_TX, (void *)RX_DATA_buffer);/* Set DMA CH-1 block length */_dma_set_data_amount(SERCOM0_DMAC_ID_TX, (uint32_t)Buffer_length);/* Get DMA CH-1 resource to set the application callback */_dma_get_channel_resource(&dma_res, SERCOM0_DMAC_ID_TX);/* Set application callback to handle the DMA CH-0 transfer done */dma_res->dma_cb.transfer_done = dmac_channel_TX_cplt_callback;/* Enable DMA CH-1 transfer complete interrupt */_dma_set_irq_state(SERCOM0_DMAC_ID_TX, DMA_TRANSFER_COMPLETE_CB, true);}int main(void){/* Initializes MCU, drivers and middleware */atmel_start_init();config_dma_channel_0();config_dma_channel_1();usart_async_enable(&USART_0);//configure_usart();/* Replace with your application code */while (1) {}}Also i've noticed theCode:#define USART_0_BUFFER_SIZE 16in thedriver_init.cfile, which i think describes the length of the FIFO buffer mentioned in the datasheet, but is this in bits or bytes?QuoteBut what are the RX and TX buffers really? When i look at the USART diagram in the datasheet i immediately notice the RX buffer but there's no TX buffer. Then does the TX buffer refer to the TX data register? Do i need to load frame after frame when i want to transmit?" alt="" class="bbc_img" />And is the USART.DATA.reg, that i used in my code, the FIFO data buffer that i want?When i run my code and watch the(Sercom *)(USART_0.device.hw))->USART.DATA.regand then send something through the UART (using my FT232) i do see it appear in the mentioned address but no matter what i send it doesn't change afterwards. For example if i send 'x' in ASCII i see 120 in the register, which is correct according to the ASCII table and if i send 200 in plain hex it does appear in the register as 200 as well.But this is all my code does so far, so i would appreciate some help.

author: ataradov
author_rank: Super Contributor
timestamp: November 18, 2020, 12:23:05 am
content: I don't know about that horrible ASF stuff. Here is an example of bare-metal code that uses DMA to work with SERCOM. The code also sleeps during the transfer, you can ignore that stuff.Generally I'm not sure what you want to do. You can't simultaneously receive and transmit the same bytes. You can receive the entire buffer, get an interrupt and then transmit the entire buffer.If you want to send the bytes out as they come in, then just use interrupts. DMA is not for this.Note that this code does not really show the best way to handle DMA interrupts. You need to save the value of the DMAC->CHID.reg on entry and restore it back on exit.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 18, 2020, 11:10:53 am
content: Quote from: ataradov on November 18, 2020, 12:23:05 amI need to process 40-bit data from three SERCOM units almost simultaneously, because any one of them could receive data at nearly the same time.Also i need as little CPU overhead as possible to maintain high speed, which is why i chose DMA.I plan to have DMA receive data in the background while the CPU processes data from previous transmission.I won't be transmitting and receiving through the same SERCOM simultaneously, for example if i receive through SERCOM0 i will transmit through SERCOM1 and SERCOM2.Thanks for the code example, i'll check it out but i'll also keep trying with the with the ASF4/START.Edit: i'll make a diagram to explain what i meanEdit2:my attachment keeps failing security checks "security checks could not be uploaded", guess i'll try other means.fixed

author: Refrigerator
author_rank: Super Contributor
timestamp: November 18, 2020, 12:08:31 pm
content: Here's the basic idea of what i'm trying to achieve.First i need to echo the received data through where i came from, this is to confirm the presence of a module. Whichever SERCOM module receives data first is the chosen one, other SERCOM modules are ignored for now.Second i need to recalculate the two separate TX buffers based on what i received in the RX buffer.Which is why to get started i tried the SERCOM RX -> MEM -> SERCOM TX approach.

author: ataradov
author_rank: Super Contributor
timestamp: November 18, 2020, 07:38:07 pm
content: Simultaneous reception is not an issue for interrupts, the device runs at 48 MHz, it has plenty of time to receive data from the holding registers of the SERCOM before a new byte arrives.I personally would just use interrupts here at least for a first iteration. Add DMA later if you actually see performance issues.DMA is not easy to work with and there is no need to add that difficulty on top of everything else.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 18, 2020, 09:24:36 pm
content: Quote from: ataradov on November 18, 2020, 07:38:07 pmIt seemed so easy on STM32 devices that i considered DMA just in all ways superior to any other data transfer method.I'd just set it up, stick a timer to it if i need to, and it just worked. No interrupts, no nothing, the DMA would just work like clockwork.Which is also why i wanted to go with DMA without giving it any second thought, but turns out it's a little more involved on the SAMD10 platform.I'm not very familiar with interrupts, are there any good primers/exmples of interrupts? (on SAMD10)

author: ataradov
author_rank: Super Contributor
timestamp: November 18, 2020, 09:29:33 pm
content: It is not hard, probably not harder than on STM, but you are trying to use ASF code, which is horrible. If you go with bare-metal code, then it should be pretty easy.The issue with DMA and UART is error handling. What if one byte gets an error and is not properly received? The whole stream would be de-synchronized.I typically stay away from DMA for applications like that. But if you think that data loss is not a problem for this application, then DMA would work fine.Here is example of a UART driver using interruptshttps://github.com/ataradov/vcp/blob/master/samd11/uart.c

author: Refrigerator
author_rank: Super Contributor
timestamp: November 19, 2020, 10:22:45 am
content: Managed to get the DMA somewhat working, at least for TX.Code:#include <atmel_start.h>#include <hal_gpio.h>#include <hal_delay.h>#include <hal_ext_irq.h>#include <hpl_dma.h>//#include "utils.h"#define TX_Bytes 12static uint8_t USART_TX_Buf = "Hello World!";void dmac_channel_0_callback(struct _dma_resource *resource){//gpio_toggle_pin_level(LED0);// if led toggles dma works - tested works/* Enable DMA CH-2 *///_dma_enable_transaction(2, false);}void config_dma_channel_0(void){struct _dma_resource *dma_res;_dma_set_source_address(0, (void *)USART_TX_Buf);_dma_set_destination_address(0, (void *)&(((Sercom *)(USART_0.device.hw))->USART.DATA.reg));_dma_set_data_amount(0, (uint32_t)TX_Bytes);_dma_get_channel_resource(&dma_res, 0);/* Set application callback to handle the DMA CH-0 transfer done */dma_res->dma_cb.transfer_done = dmac_channel_0_callback;_dma_set_irq_state(0, DMA_TRANSFER_COMPLETE_CB, true);}void USART_transmit(void){// gets herestruct io_descriptor *io;usart_async_get_io_descriptor(&USART_0, &io);// worksio_write(io, USART_TX_Buf, 12);// works}static void tx_cb_USART_0(const struct usart_async_descriptor *const io_descr)// reaches this{/* Transfer completed *///gpio_toggle_pin_level(LED0);// if led toggles TX works - IRQ tested worksgpio_toggle_pin_level(LED0);// if led toggles TX works - DMA tested actually toggles too fast to see, TX confirmed on serial port//_dma_enable_transaction(0, true);// this line here = big bug}void USART_0_setup(void){struct io_descriptor *io;static uint8_t USART_ok_msg = "USART enabled OK\r\n";usart_async_register_callback(&USART_0, USART_ASYNC_TXC_CB, tx_cb_USART_0);// works/*usart_async_register_callback(&USART_0, USART_ASYNC_RXC_CB, rx_cb);usart_async_register_callback(&USART_0, USART_ASYNC_ERROR_CB, err_cb);*/usart_async_get_io_descriptor(&USART_0, &io);// worksusart_async_enable(&USART_0);// worksio_write(io, USART_ok_msg, 18);// works}static void BUTTON_pressed(void)// works{delay_ms(100);// works//gpio_toggle_pin_level(LED0);// works//io_write(io, USART_TX_Buf, 12);// io descriptor error, no surprise//USART_transmit();usart_async_enable(&USART_0);_dma_enable_transaction(0, true);// works}void EXTERNAL_IRQ_5_setup(void)// works{ext_irq_register(BUTTON, BUTTON_pressed);// works}int main(void){/* Initializes MCU, drivers and middleware */atmel_start_init();// worksEXTERNAL_IRQ_5_setup();// worksUSART_0_setup();// worksconfig_dma_channel_0();/* Replace with your application code */while (1) {}}This code was written step-by-step. Basically i started off with just toggling an LED by using an external interrupt tied to the switch on the Xplained Mini board.I then used this interrupt to transmit "hello world!" and placed the LED toggle line in the TX complete callback. The LED toggled, meaning TX worked so i moved on to adding more stuff.I then set up DMA to dump the entire TX buffer into the TX register whenever the switch was pressed. DMA was set up to disable after each block transfer. LED toggle line was placed in the DMA complete callback and it toggled.So then i placed the LED toggle line in the TX complete callback and it toggled also, meaning that my DMA transfer was triggering the TX. I replaced the conventional TX line in the TX setup function to display "USART enabled ok".So then i hooked up my FT232 and opened realterm to see the results. Once the board powers up it sends the "USART enabled ok", then everytime i press the button it sends a "He", which seems to confirm that the TX buffer is 16 bits.But this only works when i'm in the debug mode, if i power the board up without debug mode and press the button it sends a single "He" and does not respond to button presses anymore.

author: ataradov
author_rank: Super Contributor
timestamp: November 19, 2020, 06:00:40 pm
content: I can't help with ASF code. I have no idea what is going on. Plus this code is missing a ton of DMA configuration code. Frameworks really like to spread things around.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 19, 2020, 11:21:19 pm
content: Quote from: ataradov on November 19, 2020, 06:00:40 pmThe only good thing i see with ASF is that it's convenient for generating templates quickly.And for someone like me, who misses the small details often, it speeds up stuff since i can just generate a new template.I'm the kind of person who likes restarting from scratch, each time learning something new until i can write a working program

author: Refrigerator
author_rank: Super Contributor
timestamp: November 20, 2020, 12:24:08 pm
content: Managed to get "Hello World!" to print out through DMATurns out i don't need the event system for DMA transfers at all. I just changed the DMA settings in Start.Atmel to accept SERCOM0 TX trigger and set beat size to 8 bits.Then i regenerated the template, copied my main.c over from the previous one and it works nowBut the same problem remains where it only works in debug mode, or right after i exit debug mode and it resets, but any additional power cycles result in the MCU hanging up after the first button press.Maybe this is supposed to happen i don't know.Next step now would be to set up the RX.

author: ataradov
author_rank: Super Contributor
timestamp: November 20, 2020, 05:11:25 pm
content: Quote from: Refrigerator on November 20, 2020, 12:24:08 pmIn the Atmel Studio there is a way to start a debug session without resetting the MCU. So stop the debug session and let the code get stuck. Then do Debug -> Attach to Running Target. This will start a debug session and will show you what it is doinf at the moment. See where it is stuck.Quote from: Refrigerator on November 20, 2020, 12:24:08 pmLook into the code I provided. You would see that you don't need events and you will see all the configurations you need to make. If you still want to do that using ASF, then you can just transfer those setting into the ASF project.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 26, 2020, 12:13:48 pm
content: Time to return to this project, I connected the mcu while it was hung up.And this is where it stops:Code:/*** \brief Default interrupt handler for unused IRQs.*/void Dummy_Handler(void){while (1) {}}Stepping in debug mode shows that it gets stuck between these two:Code:00000000 c0.05 lslsr0, r0, #23Code:void Dummy_Handler(void){while (1) {}}If the dummy handler is for unused interupts then why would it trigger?Edit: in debug mode it never reaches this dummy handler.I've got no clue how to fix this

author: Refrigerator
author_rank: Super Contributor
timestamp: November 26, 2020, 12:52:50 pm
content: After looking at this code:Code:/* Default empty handler */void Dummy_Handler(void);/* Debug time */void Debug_handler(void);/* Cortex-M0+ core handlers */void NonMaskableInt_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));void HardFault_Handler ( void ) __attribute__ ((weak, alias("Debug_handler"))); // changedvoid SVCall_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));void PendSV_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));void SysTick_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));I stuck in my own handler into the hard fault line and guess what it goes right there. So there seems to be a hard fault everytime an interrupt gets called

author: Refrigerator
author_rank: Super Contributor
timestamp: November 26, 2020, 12:57:15 pm
content: Commenting the DMA enable line and uncommenting my LED toggle line makes the LED toggle no matter what mode i'm in so there seems to be a hard fault happening whenever there's DMA involved

author: ataradov
author_rank: Super Contributor
timestamp: November 26, 2020, 07:10:08 pm
content: First of all, you don't need to change the weak alias, just define HardFault_Handler() in your application and it will take over the weak dummy alias.It is likely that something is misconfigured in your DMA configuration. Start with a simple un-triggered memory to memory transfer. Make that work and then one by one enable peripheral transfer.One thing to pay attention to with this DMA is that when pointing to a buffer, you need to setup a pointer to one byte past the buffer. Frameworks often try to hide that. And given that you at least get "He", the buffer should be correct.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 26, 2020, 10:36:57 pm
content: Quote from: ataradov on November 26, 2020, 07:10:08 pmThanks for the advice.I did manage to get the whole "hello world", just didn't include the screenshot. But there might also be a clue because when i press the button i receive a single "hello world" and the MCU hangs. So that at least tells me that the MCU hangs after the DMA transfer, maybe on DMA transfer complete.I will try DMA memory to memory also.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 26, 2020, 10:43:45 pm
content: Tried making a couple screenshots to show how my code works and noticed something interesting myself.The message is supposed to be "Hello World!" but i only receive "Helo World!", can't believe i missed that previously. Then i thought that maybe it skips the repeating "L" so i tried "ABCDEFGHIJKL" and it skips the "C", then i also tried "AABBCCDDEEFF" and it skips one of the B's. So there seems to be something happening after the first two bytes.

author: ataradov
author_rank: Super Contributor
timestamp: November 26, 2020, 10:51:51 pm
content: This is very strange. I have never seen anything like that. Something is not configured correctly, but I can't even imagine what that can be.For the hard fault, look at the DMA interrupt handler, and see if that is called before the HF.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 27, 2020, 01:04:05 pm
content: I placed a breakpoint on the DMA transfer complete callback and it doesn't reach the callback before it triggers the hard fault.Also i decresed the number of bytes to transmit, first only by one, which resulted in the last character not being printed.Then i decresed it to only one character and it printed out only one as expected. But it would still hard fault regardless of how many characters i sent, everytime it hard faults before it reaches the DMA transfer complete callback.I'm wondering what would make it work only in debug mode, perhaps in debug mode the clocks are different, maybe my clocks aren't set up correctly?

author: ataradov
author_rank: Super Contributor
timestamp: November 27, 2020, 07:52:35 pm
content: Your clocks are fine.Can you share the complete project?

author: Refrigerator
author_rank: Super Contributor
timestamp: November 27, 2020, 09:58:01 pm
content: I tried attaching my Atmel Studio project but looks like my project file is too large.

author: ataradov
author_rank: Super Contributor
timestamp: November 27, 2020, 10:01:23 pm
content: Find some other way to share it. There are many file sharing sites.And also clean the project before archiving to remove the binary stuff. I don't need 30 MBytes of object files.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 27, 2020, 10:41:59 pm
content: I made a Github repository, first time using this site. It's set to public so i hope it doesn't get "forked".https://github.com/SpaghettiCodeMasterChef/ATSAMD10D-DMA-UART-hard-fault-debugging/blob/main/buttonDMATXtest4IRQ.zip

author: ataradov
author_rank: Super Contributor
timestamp: November 27, 2020, 10:54:11 pm
content: The first obvious thing I don't like about that code is simultaneous interrupt initialization for the USART and use of the DMA. Your USART interrupt may randomly trigger when DMA works.Don't call usart_async_register_callback() and don't print the string using non-DMA version.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 27, 2020, 11:34:31 pm
content: I commented out a bunch of non-DMA related code but the results are still the sameSkips the third character and hard faults on transfer complete before reaching DMA transfer complete callback.Edit: out of all this code onlyusart_async_enable(&USART_0);is left uncommented.Code:// void USART_transmit(void){// gets here//struct io_descriptor *io;//usart_async_get_io_descriptor(&USART_0, &io);// works//io_write(io, USART_TX_Buf, 12);// works// }// static void tx_cb_USART_0(const struct usart_async_descriptor *const io_descr)// reaches this// {///* Transfer completed */////gpio_toggle_pin_level(LED0);// if led toggles TX works - IRQ tested works//gpio_toggle_pin_level(LED0);// if led toggles TX works - DMA tested actually toggles too fast to see, TX confirmed on serial port////_dma_enable_transaction(0, true);// this line here = big bug// }void USART_0_setup(void){//struct io_descriptor *io;//static uint8_t USART_ok_msg = "USART enabled OK\r\n";//usart_async_register_callback(&USART_0, USART_ASYNC_TXC_CB, tx_cb_USART_0);// works/*usart_async_register_callback(&USART_0, USART_ASYNC_RXC_CB, rx_cb);usart_async_register_callback(&USART_0, USART_ASYNC_ERROR_CB, err_cb);*///usart_async_get_io_descriptor(&USART_0, &io);// worksusart_async_enable(&USART_0);// works//io_write(io, USART_ok_msg, 18);// works}

author: ataradov
author_rank: Super Contributor
timestamp: November 27, 2020, 11:42:57 pm
content: Ok, then you need to debug further. The easiest way is to at least figure out what causes the hard fault.In your Debug_handler() instead of an infinite loop, put asm("nop"); and place a breakpoint on that. When execution hits that breakpoint, single step and let it exit the handler. Where does it go?

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 09:26:02 am
content: It desn't really go very far, only the single line in the screenshot and then back to the Debug_handler.

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 09:31:37 am
content: Well, this is calling a null pointer. Some callback got overwritten or never set in a first place.The way to debug this is look at register values when in hard fault handler. Look for something that looks like address in the flash or peripheral pointer. Basically try to find out what code was likely running.And also, set the breakpoint right in the DMA handler and step through that.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 03:43:23 pm
content: I quickly whipped up another project on Start.Atmel this time to test DMA memory to memory transfer and everything is ok.DMA transfer works and no characters are skipped, debug and freerun modes behave the same.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 04:03:56 pm
content: Also digging through registers is not my forte so before that i'll try some other things out, for example other SERCOM units.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 04:43:09 pm
content: Ok time for more weirdness.I remade my original project from scratch and added some additional functions. Now the code has two DMA channels enabled.DMA0 transfers memory to memory, while DMA1 transfers memory to peripheral (SERCOM0). Also DMA1 transfer is enabled when button is pressed in the IRQ handler and DMA0 is triggered in DMA1 transfer complete callback.And..... i can't get it to hang up anymore but it still skips the third characterWhat's more, when i plug the USB lead in the Ext IRQ triggers and then also triggers the DMA transfer but guess what it doesn't skip the third character. Instead it skips the first oneAny more button presses result in the same third character skip, but hey at least it doesn't hard fault anymoreThe TX string this time is "1234567890" and in the screenshot you can see "234567890", which appeared on power-up. Followed by "124567890", which printed out after i pressed the button.Ps: the unvanted IRQ trigger is probbaly because the MCU boots up before the power stabilises, because adding a delay before IRQ enable stops this behaviour.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 05:17:46 pm
content: SERCOM1 behaves exactly the same, third character gets skipped but i don't notice any other abnormalities, also i haven't gotten the MCU to hard fault yet.I also made it transmit a part from the bee movie script just for the kicks and to see how well it handles long messages.Next step is to change the DMA trigger to SW only and try sending the message character-by-character.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 06:15:36 pm
content: Changed the DMA trigger to SW only (one character per button press) and it transfers with no problems.Maybe the clocks are incorrect? I'll try changing them.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 06:46:45 pm
content: Changing clocks did nothing, changing baud rate did nothing also.Changing DMA priority from 0 to 3 also did nothing.But changing data transfer to 16 bits invents a new language.

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 06:47:11 pm
content: I would check with the scope what actually happens on the TX line.And try to set a different baudrate. It is possible that the characters are missing on the receive side due to baudrate mismatch.Ok, you did that.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 06:49:23 pm
content: Quote from: ataradov on November 28, 2020, 06:47:11 pmI tried changing baudrate from 9600 to 115200 but to no avail. I'll try hooking my scope up, but unfortunately it doesn't have serial decoding capability.

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 06:50:19 pm
content: Just capture the waveform and decode with your eyes.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 07:42:56 pm
content: My eyes aren't very good so i tried "decoding" the waveform with paint.The first two characters are "Ac" but the rest are gibberish, perhaps i just suck at hand decoding ASCII?

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 07:49:26 pm
content: Start with a simple sequence of numbers and repeating characters. Or a string "UUUUUUUUUUUU", which should produce square wave.Basically experiment and see how that reflects on the output.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 07:53:51 pm
content: Well it turns out i was counting my bits from the wrong direction.Ps:i'm going to ebay right now to get myself a cheap logic analyzer because this is painful.order placed

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 08:02:38 pm
content: So the character is really lost. I'll try that project on Monday when I'm at work.But also, share your latest project that reproduces this result.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 08:19:27 pm
content: Quote from: ataradov on November 28, 2020, 08:02:38 pmThanks for the help.Also every project i've tried so far does this, with the exception of the SW only trigger.I've also tried transmitting the "UUUUUUU" (7 characters), but i only receive "UUUUUU" (6 characters).Scope does produce a nice square wave.I'll upload a project that works, because i do have a few that i "debugged" into not workingYet this MCU continues to be the bane of my existence.

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 08:23:20 pm
content: I doubt it is the MCU itself, It is likely some ASF issue. You can try the bare-metal code I attached earlier, it will probably just work.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 08:34:16 pm
content: Quote from: ataradov on November 28, 2020, 08:23:20 pmCoding in bare metal is still a little scary for me hence why i try to stay away from it.https://github.com/SpaghettiCodeMasterChef/ATSAMD10D-DMA-UART-hard-fault-debugging/blob/main/DMA_mem_to_mem_to_UART_w_ExtIRQ_test5(1).rar

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 09:11:53 pm
content: Quote from: Refrigerator on November 28, 2020, 08:34:16 pmI really don't get it. Looks at what all your APIs are doing. They are literally set one or two registers via 100 indirections.Here is a complete bare-metal project that does what you want with bare-metal. It works, which confirms that the hardware is fine.Don't be afraid of it, it is much-much easier than stupid frameworks. Always.The project prints "Hello, world!" using blocking calls, and then prints a test string using DMA every time you press a button on the D10 Xmini.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 10:58:51 pm
content: Quote from: ataradov on November 28, 2020, 09:11:53 pmWorks greatMy PCB's arrive on wednesday so i'll have to get my code finished and running until then and i'm sure this demo will help me a lot.Also i'm pretty sure i can use my SAMD10 Xplain mini as a standalone SWD programmer, but i assume i'll have to desolder the SAMD10 to not interfere, right?I'll use it to program/debug other SAMD10 MCU's so compatibility should be fine.

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 11:06:31 pm
content: Quote from: Refrigerator on November 28, 2020, 10:58:51 pmThere are series resistors you can remove instead of the IC. But those resistors also disconnect "EXTERNAL DEBUGGER" connector.So you can either remove the resistors and use resulting pads to bring out the signals. Or remove the IC and use the connector to program the target.If your device is not exactly the same package as on the Xmini, you will probably have to set a checkbox to ignore the device type in the AS.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 11:29:20 pm
content: Quote from: ataradov on November 28, 2020, 11:06:31 pmIt's the exact same micro (ATSAMD10D14MUT).Also there's an SWD pinout next to the LED0, i wonder would this stay connected if i removed the resistors?I'll have to check the schematic of this board.Edit: looks like the SWD is for input not output.Quote

author: ataradov
author_rank: Super Contributor
timestamp: November 28, 2020, 11:34:14 pm
content: That's the ""EXTERNAL DEBUGGER" connector. If you remove the device from the boars, you can use that to connect to your board.The intended use is for the external debugger, but the lines are connected, so pins from the mEDBG go to the target and that connector. If there is no target, then they still go to the connector.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 28, 2020, 11:42:58 pm
content: Quote from: ataradov on November 28, 2020, 11:34:14 pmYep, i just checked the schematic and if i remove the MCU then those pins become my SWD programmer pins.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 29, 2020, 07:03:01 pm
content: I have one more question, this time not about DMA.I need to set my MCU to 48MHz but i can't figure out how to do it on bare metal.Would it be possible to set the CPU clocks using the Start.atmel and do the rest on bare metal?Or would ASF4 dump too much garbage in my project?

author: ataradov
author_rank: Super Contributor
timestamp: November 29, 2020, 07:21:50 pm
content: Here is example code that sets 48 MHz clock using 32 kHz oscillator as a reference. This code is for D21, but it should work on D10 as well.If your hardware does not have a 32 kHz crystal installed, then replace GCLK_SOURCE_XOSC32K with GCLK_SOURCE_OSCULP32K. Or enable OSC32K and use GCLK_SOURCE_OSC32K, which is more accurate.Code:static void sys_init(void){NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_RWS(2);SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_ENABLE | SYSCTRL_OSC32K_STARTUP(3) |SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_RUNSTDBY | SYSCTRL_XOSC32K_AAMPEN |SYSCTRL_XOSC32K_EN32K;while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY));GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC32K) |GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SYSCTRL_GCLK_ID_DFLL48) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(1);SYSCTRL->INTFLAG.reg = SYSCTRL_INTFLAG_BOD33RDY | SYSCTRL_INTFLAG_BOD33DET | SYSCTRL_INTFLAG_DFLLRDY;SYSCTRL->DFLLCTRL.reg = 0; // See Errata 9905while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY));SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(1) | SYSCTRL_DFLLMUL_FSTEP(1) |SYSCTRL_DFLLMUL_MUL(1465 - 1);SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE | SYSCTRL_DFLLCTRL_MODE;while (0 == (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY));GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) | GCLK_GENCTRL_SRC(GCLK_SOURCE_DFLL48M) |GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_OE | GCLK_GENCTRL_IDC;while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);}Also, my GitHub is full of code for those deviceshttps://github.com/ataradov/. Keep in mind that most of the code for D21 or D11 will also work on D10.You can use ASF for basic setup and then write in bare-metal, but it is even more frustrating than using pure bare-metal code. It is better to make configuration in Star, generate the code, and just look at it and copy relevant parts. This will also let you appreciate just how bad generated code is.

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2020, 10:36:48 pm
content: The issue with your code is the last "true" in the call to _dma_enable_transaction(). You do not need a software trigger. Transmitter being empty will automatically trigger the transfer. In this case you get a double trigger - the software one and the hardware one at the same time. This causes one byte to be skipped.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 30, 2020, 11:08:43 pm
content: Quote from: ataradov on November 30, 2020, 10:36:48 pmIf TX empty triggers DMA then wouldn't that mean that DMA would transmit continuously?Then are my DMA settings incorrect?I assumed that DMA transfer goes like this:1. I trigger the DMA.2. DMA gets retriggered by TX empty.3. once DMA buffer is empty it disables.And then i go back to step 1 by triggering DMA again.Should DMA disable after transfer complete be changed to channel suspend?

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2020, 11:15:03 pm
content: Quote from: Refrigerator on November 30, 2020, 11:08:43 pmOnce it writes the data register, the TX empty will become 0 and it won't be able to write more until the byte is sent. And it will not transmit past the specified count, so the value of the TX Empty is irrelevant after that. So in a sense, it will transmit continuously moderated by the UART baudrate.Quote from: Refrigerator on November 30, 2020, 11:08:43 pmYour other settings are correct. The only thing that is wrong is the way you trigger it.Quote from: Refrigerator on November 30, 2020, 11:08:43 pmDepends on what you mean by trigger. You enable the channel. It gets auto-triggered by the peripheral.Quote from: Refrigerator on November 30, 2020, 11:08:43 pmThat is correct. And to start a new transfer you enable the channel again. Without software triggering.Quote from: Refrigerator on November 30, 2020, 11:08:43 pmNo. Everything in your code is correct except for that one parameter in the enable call.

author: Refrigerator
author_rank: Super Contributor
timestamp: November 30, 2020, 11:49:01 pm
content: _dma_enable_transaction(1, false); solved the problemI think the main issue was that i thought_dma_enable_transaction(1, true);meant DMA channel1 enableand_dma_enable_transaction(1, false);meant DMA channel1 disable.When in reality the true/false means whether the software trigger is sent or not.So it was a mistake on my part.

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2020, 11:51:21 pm
content: Quote from: Refrigerator on November 30, 2020, 11:49:01 pmThat's exactly what you get when using frameworks. You never know what your code is actually doing unless you study it in great details. At which point you might as well just write it.Plus by writing bare metal code you are acquiring a skill that is transferable to any other device basically forever.