通过DMA使用SPI发送数据

huangapple go评论97阅读模式
英文:

Send data through SPI with DMA

问题

I need to send data as fast as possible from an Arduino DUE to an external DAC. To do so, I use DMA & SPI, and I want DMA to fetch data from memory and send it to SPI, which will relay it via its Master Output Slave Input. So far, I did a DMA transfer from one variable to another, and it worked perfectly. I'm using the same code but changing the address to SPI_TDR (Transmit Data Register), unfortunately, it's not working. I guess the address is not correct, but if so, what should I do?

Here is my code:

  1. #include <dmac.h>
  2. #include <SPI.h>
  3. #define DMA_CH 0 // DMA Channel number
  4. #define DMA_BUF_SIZE 32 // DMA memory size
  5. uint32_t g_dma_buf2[DMA_BUF_SIZE];
  6. void setup() {
  7. Serial.begin(9600);
  8. SPI.begin();
  9. SPI0->SPI_WPMR = 0x53504900; // Protection key
  10. SPI0->SPI_IDR = 0x0000070F; // Disable interrupts
  11. SPI0->SPI_MR = SPI_MR_MSTR | SPI_MR_PS; // SPI master
  12. }
  13. void loop() {
  14. Serial.println("+++++");
  15. pmc_enable_periph_clk(ID_DMAC);
  16. uint32_t i;
  17. uint32_t cfg;
  18. dma_transfer_descriptor_t desc;
  19. for (i = 0; i < DMA_BUF_SIZE; i++) {
  20. g_dma_buf2[i] = i;
  21. Serial.print(g_dma_buf2[i]);
  22. }
  23. Serial.println();
  24. dmac_init(DMAC);
  25. dmac_set_priority_mode(DMAC, DMAC_PRIORITY_ROUND_ROBIN);
  26. dmac_enable(DMAC);
  27. cfg = DMAC_CFG_SOD_ENABLE | DMAC_CFG_AHB_PROT(1) | DMAC_CFG_FIFOCFG_ALAP_CFG; // CFG register config
  28. dmac_channel_set_configuration(DMAC, DMA_CH, cfg);
  29. desc.ul_source_addr = (uint32_t)g_dma_buf2;
  30. desc.ul_destination_addr = SPI0->SPI_TDR;
  31. desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(DMA_BUF_SIZE) | DMAC_CTRLA_SRC_WIDTH_WORD | DMAC_CTRLA_DST_WIDTH_WORD;
  32. desc.ul_ctrlB = DMAC_CTRLB_SRC_DSCR_FETCH_DISABLE | DMAC_CTRLB_DST_DSCR_FETCH_DISABLE | DMAC_CTRLB_FC_MEM2MEM_DMA_FC | DMAC_CTRLB_SRC_INCR_INCREMENTING | DMAC_CTRLB_DST_INCR_FIXED;
  33. desc.ul_descriptor_addr = 0;
  34. SPI_Enable(SPI0);
  35. dmac_channel_multi_buf_transfer_init(DMAC, DMA_CH, &desc);
  36. dmac_channel_enable(DMAC, DMA_CH);
  37. Serial.println("*****");
  38. while (!dmac_channel_is_transfer_done(DMAC, DMA_CH)) { Serial.print('X'); }
  39. Serial.print("SR : "); Serial.println(SPI0->SPI_SR, HEX);
  40. Serial.print("TDR : "); Serial.println(SPI0->SPI_TDR, HEX);
  41. Serial.print("PSR : "); Serial.println(PIOA->PIO_PSR, HEX); // PIO_SODR
  42. Serial.print("OSR : "); Serial.println(PIOA->PIO_OSR, HEX);
  43. Serial.println(DMAC->DMAC_CH_NUM[0].DMAC_SADDR , HEX);
  44. Serial.println(DMAC->DMAC_CH_NUM[0].DMAC_DADDR, HEX);
  45. Serial.println("-----");
  46. }

I mainly use this example: Microchip DMA Controller Application Note.

And here is the datasheet of the microcontroller: Microchip Cortex-M3 Microcontroller Datasheet.

You can see at the bottom of my code several prints, and from them, I had many ideas: does the PIO block the data? Could the address PIO_PA26A_SPI0_MOSI work? Could the SPI block the data because conditions are not met?

Any idea is welcomed; I've been working on this for some time now.

Edit: SPI is not a necessity; the idea is to send data without a length limit (unlike UART). I'm considering using SSC.

英文:

I need to send data as fast as possible from an Arduino DUE to an extern DAC. To do so I use DMA & SPI and I want DMA to fetch data from the memory and send it to the SPI which will just relay it via its Master Output Slave input.
So far I did a DMA transfer from a variable to another, woked perfectly. I'm using the same code but change the address as SPI_TDR (Transmit Data Register), unfortunatly it's not working. I guess the address is not good but if so what should I do ?

Here is my code :

  1. #include &lt;dmac.h&gt;
  2. #include &lt;SPI.h&gt;
  3. #define DMA_CH 0 //N&#176; Canal du DMA
  4. #define DMA_BUF_SIZE 32 //Taille m&#233;moire DMA
  5. uint32_t g_dma_buf2[DMA_BUF_SIZE];
  6. void setup() {
  7. Serial.begin(9600);
  8. SPI.begin();
  9. SPI0-&gt;SPI_WPMR = 0x53504900; //Protection key
  10. SPI0-&gt;SPI_IDR = 0x0000070F; //Desactivation interrupts
  11. SPI0-&gt;SPI_MR = SPI_MR_MSTR | SPI_MR_PS; //SPI master
  12. }
  13. void loop() {
  14. Serial.println(&quot;+++++&quot;);
  15. pmc_enable_periph_clk(ID_DMAC);
  16. uint32_t i;
  17. uint32_t cfg;
  18. dma_transfer_descriptor_t desc;
  19. for (i = 0; i &lt; DMA_BUF_SIZE; i++) {
  20. g_dma_buf2[i] = i;
  21. Serial.print(g_dma_buf2[i]);
  22. }
  23. Serial.println();
  24. dmac_init(DMAC);
  25. dmac_set_priority_mode(DMAC, DMAC_PRIORITY_ROUND_ROBIN);
  26. dmac_enable(DMAC);
  27. cfg = DMAC_CFG_SOD_ENABLE | DMAC_CFG_AHB_PROT(1) | DMAC_CFG_FIFOCFG_ALAP_CFG; //Config registre CFG
  28. dmac_channel_set_configuration(DMAC, DMA_CH, cfg);
  29. desc.ul_source_addr = (uint32_t)g_dma_buf2;
  30. desc.ul_destination_addr = SPI0-&gt;SPI_TDR;
  31. desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(DMA_BUF_SIZE) | DMAC_CTRLA_SRC_WIDTH_WORD | DMAC_CTRLA_DST_WIDTH_WORD;
  32. desc.ul_ctrlB = DMAC_CTRLB_SRC_DSCR_FETCH_DISABLE | DMAC_CTRLB_DST_DSCR_FETCH_DISABLE | DMAC_CTRLB_FC_MEM2MEM_DMA_FC | DMAC_CTRLB_SRC_INCR_INCREMENTING | DMAC_CTRLB_DST_INCR_FIXED;
  33. desc.ul_descriptor_addr = 0;
  34. SPI_Enable(SPI0);
  35. dmac_channel_multi_buf_transfer_init(DMAC, DMA_CH, &amp;desc);
  36. dmac_channel_enable(DMAC, DMA_CH);
  37. Serial.println(&quot;*****&quot;);
  38. while (!dmac_channel_is_transfer_done(DMAC, DMA_CH)) { Serial.print(&#39;X&#39;); }
  39. Serial.print(&quot;SR : &quot;); Serial.println(SPI0-&gt;SPI_SR, HEX);
  40. Serial.print(&quot;TDR : &quot;); Serial.println(SPI0-&gt;SPI_TDR, HEX);
  41. Serial.print(&quot;PSR : &quot;); Serial.println(PIOA-&gt;PIO_PSR, HEX); //PIO_SODR
  42. Serial.print(&quot;OSR : &quot;); Serial.println(PIOA-&gt;PIO_OSR, HEX);
  43. Serial.println(DMAC-&gt;DMAC_CH_NUM[0].DMAC_SADDR , HEX);
  44. Serial.println(DMAC-&gt;DMAC_CH_NUM[0].DMAC_DADDR, HEX);
  45. Serial.println(&quot;-----&quot;);
  46. }

I use mainly this example : https://ww1.microchip.com/downloads/en/Appnotes/Atmel-42291-SAM3A-3U-3X-4E-DMA-Controller-DMAC_ApplicationNote_AT07892.pdf#_OPENTOPIC_TOC_PROCESSING_d91e3076
And here is the datasheet of the µc : https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf

You can see at the bottom of my code sevral prints, from them I had many idea : does the PIO could block the data ? Does the address PIO_PA26A_SPI0_MOSI could work ? Could the SPI block the data because conditions are not met ?

Any idea is welcomed, I'm on this for some time now.

Edit : SPI is not a necessity, the idea is to send data without length limit (unlike UART). I'm considering using SSC.

答案1

得分: 0

我设法解决了,但不是用SPI,而是使用微控制器的SSC(它支持I²S并自动使用DMA):

  1. uint32_t liste[] PROGMEM = { 0x8F66F1, 0x0, 0xAAAAAB }; //要发送的数据
  2. #define DMA_BUF_SIZE (sizeof(liste) / sizeof(liste[0])) //DMA缓冲区大小
  3. uint32_t i;
  4. void setup() {
  5. uint clk_div = 0x90; //所需的时钟分频器
  6. clk_div = (clk_div / 24) / DMA_BUF_SIZE;
  7. clk_div = floor(clk_div + 0.5);
  8. clk_div = clk_div * 24 * DMA_BUF_SIZE; //调整时钟分频器 -&gt; MCK/2*clk_div
  9. PMC-&gt;PMC_WPMR = 0x504D4300; //禁用PMC保护
  10. PMC-&gt;PMC_PCER0 = (1 &lt;&lt; ID_SSC); //启用SSC时钟
  11. PMC-&gt;PMC_SCER |= 0x100; //启用时钟
  12. PIOA-&gt;PIO_WPMR = 0x50494F00; //禁用Port I/O A保护
  13. PIOA-&gt;PIO_PDR = PIO_PDR_P14 | PIO_PDR_P15 | PIO_PDR_P16; //I/O由外设控制
  14. PIOA-&gt;PIO_ABSR |= PIO_PA14B_TK | PIO_PA15B_TF | PIO_PA16B_TD; //将I/O分配给SSC
  15. SSC-&gt;SSC_CR = SSC_CR_RXDIS | SSC_CR_TXDIS | SSC_CR_SWRST; //禁用和重置SSC
  16. SSC-&gt;SSC_WPMR = 0x53534300; //禁用SSC保护
  17. SSC-&gt;SSC_IDR = 0xFFFFFFFF; //禁用中断
  18. SSC-&gt;SSC_IER = 0x00000000; //禁用中断(另一种方式)
  19. SSC-&gt;SSC_CMR = clk_div; //时钟管理
  20. SSC-&gt;SSC_TFMR = SSC_TFMR_DATLEN(0x18) | SSC_TFMR_MSBF | SSC_TFMR_DATNB(0); //数据传输管理
  21. SSC-&gt;SSC_TCMR = SSC_TCMR_CKS_MCK | SSC_TCMR_CKO_CONTINUOUS | SSC_TCMR_START_CONTINUOUS | SSC_TCMR_STTDLY(0); //传输期间的时钟管理
  22. SSC-&gt;SSC_CR = SSC_CR_TXEN; //启用SSC传输
  23. }
  24. void loop() {
  25. for (i = 0; i &lt; DMA_BUF_SIZE; i++) {
  26. ssc_write((Ssc*)SSC, (uint32_t)liste[i]); //SSC传输
  27. }
  28. }
英文:

I managed to solve it but not with SPI, I use the SSC of the µcontroller (which does I²S and uses DMA automatically) :

  1. uint32_t liste[] PROGMEM = { 0x8F66F1, 0x0, 0xAAAAAB }; //Data to send
  2. #define DMA_BUF_SIZE (sizeof(liste) / sizeof(liste[0])) //Size DMA buffer
  3. uint32_t i;
  4. void setup() {
  5. uint clk_div = 0x90; //Clock divider wanted
  6. clk_div = (clk_div / 24) / DMA_BUF_SIZE;
  7. clk_div = floor(clk_div + 0.5);
  8. clk_div = clk_div * 24 * DMA_BUF_SIZE; //Clock divider adjusted -&gt; MCK/2*clk_div
  9. PMC-&gt;PMC_WPMR = 0x504D4300; //Desactivation protection PMC
  10. PMC-&gt;PMC_PCER0 = (1 &lt;&lt; ID_SSC); //Activation SSC clock
  11. PMC-&gt;PMC_SCER |= 0x100; //Activation clock
  12. PIOA-&gt;PIO_WPMR = 0x50494F00; //Desactivation protection Port I/O A
  13. PIOA-&gt;PIO_PDR = PIO_PDR_P14 | PIO_PDR_P15 | PIO_PDR_P16; //I/O controlled by the peripheral
  14. PIOA-&gt;PIO_ABSR |= PIO_PA14B_TK | PIO_PA15B_TF | PIO_PA16B_TD; //Assignation of I/O to SSC
  15. SSC-&gt;SSC_CR = SSC_CR_RXDIS | SSC_CR_TXDIS | SSC_CR_SWRST; //Desactivation and reset SSC
  16. SSC-&gt;SSC_WPMR = 0x53534300; //Desactivation protection SSC
  17. SSC-&gt;SSC_IDR = 0xFFFFFFFF; //Desactivation Interrupts
  18. SSC-&gt;SSC_IER = 0x00000000; //Desactivation Interrupts bis
  19. SSC-&gt;SSC_CMR = clk_div; //Clock management
  20. SSC-&gt;SSC_TFMR = SSC_TFMR_DATLEN(0x18) | SSC_TFMR_MSBF | SSC_TFMR_DATNB(0); //Data transfert management
  21. SSC-&gt;SSC_TCMR = SSC_TCMR_CKS_MCK | SSC_TCMR_CKO_CONTINUOUS | SSC_TCMR_START_CONTINUOUS | SSC_TCMR_STTDLY(0); //Clock during transfert management
  22. SSC-&gt;SSC_CR = SSC_CR_TXEN; //Activation transmission SSC
  23. }
  24. void loop() {
  25. for (i = 0; i &lt; DMA_BUF_SIZE; i++) {
  26. ssc_write((Ssc*)SSC, (uint32_t)liste[i]); //Transmission SSC
  27. }
  28. }

In case someone try the same thing.

huangapple
  • 本文由 发表于 2023年4月17日 22:37:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76036337.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定