Teensy 4.1 core updated for C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1077 satır
33KB

  1. #ifndef DMAChannel_h_
  2. #define DMAChannel_h_
  3. #include "kinetis.h"
  4. // Discussion about DMAChannel is here:
  5. // http://forum.pjrc.com/threads/25778-Could-there-be-something-like-an-ISR-template-function/page3
  6. #define DMACHANNEL_HAS_BEGIN
  7. #define DMACHANNEL_HAS_BOOLEAN_CTOR
  8. // The channel allocation bitmask is accessible from "C" namespace,
  9. // so C-only code can reserve DMA channels
  10. #ifdef __cplusplus
  11. extern "C" {
  12. #endif
  13. extern uint16_t dma_channel_allocated_mask;
  14. #ifdef __cplusplus
  15. }
  16. #endif
  17. #ifdef __cplusplus
  18. // known libraries with DMA usage (in need of porting to this new scheme):
  19. //
  20. // https://github.com/PaulStoffregen/Audio
  21. // https://github.com/PaulStoffregen/OctoWS2811
  22. // https://github.com/pedvide/ADC
  23. // https://github.com/duff2013/SerialEvent
  24. // https://github.com/pixelmatix/SmartMatrix
  25. // https://github.com/crteensy/DmaSpi <-- DmaSpi has adopted this scheme
  26. /****************************************************************/
  27. /** Teensy 3.0 & 3.1 **/
  28. /****************************************************************/
  29. #if defined(KINETISK)
  30. class DMABaseClass {
  31. public:
  32. typedef struct __attribute__((packed, aligned(4))) {
  33. volatile const void * volatile SADDR;
  34. int16_t SOFF;
  35. union { uint16_t ATTR;
  36. struct { uint8_t ATTR_DST; uint8_t ATTR_SRC; }; };
  37. union { uint32_t NBYTES; uint32_t NBYTES_MLNO;
  38. uint32_t NBYTES_MLOFFNO; uint32_t NBYTES_MLOFFYES; };
  39. int32_t SLAST;
  40. volatile void * volatile DADDR;
  41. int16_t DOFF;
  42. union { volatile uint16_t CITER;
  43. volatile uint16_t CITER_ELINKYES; volatile uint16_t CITER_ELINKNO; };
  44. int32_t DLASTSGA;
  45. volatile uint16_t CSR;
  46. union { volatile uint16_t BITER;
  47. volatile uint16_t BITER_ELINKYES; volatile uint16_t BITER_ELINKNO; };
  48. } TCD_t;
  49. TCD_t *TCD;
  50. /***************************************/
  51. /** Data Transfer **/
  52. /***************************************/
  53. // Use a single variable as the data source. Typically a register
  54. // for receiving data from one of the hardware peripherals is used.
  55. void source(volatile const signed char &p) { source(*(volatile const uint8_t *)&p); }
  56. void source(volatile const unsigned char &p) {
  57. TCD->SADDR = &p;
  58. TCD->SOFF = 0;
  59. TCD->ATTR_SRC = 0;
  60. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 1;
  61. TCD->SLAST = 0;
  62. }
  63. void source(volatile const signed short &p) { source(*(volatile const uint16_t *)&p); }
  64. void source(volatile const unsigned short &p) {
  65. TCD->SADDR = &p;
  66. TCD->SOFF = 0;
  67. TCD->ATTR_SRC = 1;
  68. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 2;
  69. TCD->SLAST = 0;
  70. }
  71. void source(volatile const signed int &p) { source(*(volatile const uint32_t *)&p); }
  72. void source(volatile const unsigned int &p) { source(*(volatile const uint32_t *)&p); }
  73. void source(volatile const signed long &p) { source(*(volatile const uint32_t *)&p); }
  74. void source(volatile const unsigned long &p) {
  75. TCD->SADDR = &p;
  76. TCD->SOFF = 0;
  77. TCD->ATTR_SRC = 2;
  78. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 4;
  79. TCD->SLAST = 0;
  80. }
  81. // Use a buffer (array of data) as the data source. Typically a
  82. // buffer for transmitting data is used.
  83. void sourceBuffer(volatile const signed char p[], unsigned int len) {
  84. sourceBuffer((volatile const uint8_t *)p, len); }
  85. void sourceBuffer(volatile const unsigned char p[], unsigned int len) {
  86. TCD->SADDR = p;
  87. TCD->SOFF = 1;
  88. TCD->ATTR_SRC = 0;
  89. TCD->NBYTES = 1;
  90. TCD->SLAST = -len;
  91. TCD->BITER = len;
  92. TCD->CITER = len;
  93. }
  94. void sourceBuffer(volatile const signed short p[], unsigned int len) {
  95. sourceBuffer((volatile const uint16_t *)p, len); }
  96. void sourceBuffer(volatile const unsigned short p[], unsigned int len) {
  97. TCD->SADDR = p;
  98. TCD->SOFF = 2;
  99. TCD->ATTR_SRC = 1;
  100. TCD->NBYTES = 2;
  101. TCD->SLAST = -len;
  102. TCD->BITER = len / 2;
  103. TCD->CITER = len / 2;
  104. }
  105. void sourceBuffer(volatile const signed int p[], unsigned int len) {
  106. sourceBuffer((volatile const uint32_t *)p, len); }
  107. void sourceBuffer(volatile const unsigned int p[], unsigned int len) {
  108. sourceBuffer((volatile const uint32_t *)p, len); }
  109. void sourceBuffer(volatile const signed long p[], unsigned int len) {
  110. sourceBuffer((volatile const uint32_t *)p, len); }
  111. void sourceBuffer(volatile const unsigned long p[], unsigned int len) {
  112. TCD->SADDR = p;
  113. TCD->SOFF = 4;
  114. TCD->ATTR_SRC = 2;
  115. TCD->NBYTES = 4;
  116. TCD->SLAST = -len;
  117. TCD->BITER = len / 4;
  118. TCD->CITER = len / 4;
  119. }
  120. // Use a circular buffer as the data source
  121. void sourceCircular(volatile const signed char p[], unsigned int len) {
  122. sourceCircular((volatile const uint8_t *)p, len); }
  123. void sourceCircular(volatile const unsigned char p[], unsigned int len) {
  124. TCD->SADDR = p;
  125. TCD->SOFF = 1;
  126. TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3);
  127. TCD->NBYTES = 1;
  128. TCD->SLAST = 0;
  129. TCD->BITER = len;
  130. TCD->CITER = len;
  131. }
  132. void sourceCircular(volatile const signed short p[], unsigned int len) {
  133. sourceCircular((volatile const uint16_t *)p, len); }
  134. void sourceCircular(volatile const unsigned short p[], unsigned int len) {
  135. TCD->SADDR = p;
  136. TCD->SOFF = 2;
  137. TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 1;
  138. TCD->NBYTES = 2;
  139. TCD->SLAST = 0;
  140. TCD->BITER = len / 2;
  141. TCD->CITER = len / 2;
  142. }
  143. void sourceCircular(volatile const signed int p[], unsigned int len) {
  144. sourceCircular((volatile const uint32_t *)p, len); }
  145. void sourceCircular(volatile const unsigned int p[], unsigned int len) {
  146. sourceCircular((volatile const uint32_t *)p, len); }
  147. void sourceCircular(volatile const signed long p[], unsigned int len) {
  148. sourceCircular((volatile const uint32_t *)p, len); }
  149. void sourceCircular(volatile const unsigned long p[], unsigned int len) {
  150. TCD->SADDR = p;
  151. TCD->SOFF = 4;
  152. TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 2;
  153. TCD->NBYTES = 4;
  154. TCD->SLAST = 0;
  155. TCD->BITER = len / 4;
  156. TCD->CITER = len / 4;
  157. }
  158. // Use a single variable as the data destination. Typically a register
  159. // for transmitting data to one of the hardware peripherals is used.
  160. void destination(volatile signed char &p) { destination(*(volatile uint8_t *)&p); }
  161. void destination(volatile unsigned char &p) {
  162. TCD->DADDR = &p;
  163. TCD->DOFF = 0;
  164. TCD->ATTR_DST = 0;
  165. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 1;
  166. TCD->DLASTSGA = 0;
  167. }
  168. void destination(volatile signed short &p) { destination(*(volatile uint16_t *)&p); }
  169. void destination(volatile unsigned short &p) {
  170. TCD->DADDR = &p;
  171. TCD->DOFF = 0;
  172. TCD->ATTR_DST = 1;
  173. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 2;
  174. TCD->DLASTSGA = 0;
  175. }
  176. void destination(volatile signed int &p) { destination(*(volatile uint32_t *)&p); }
  177. void destination(volatile unsigned int &p) { destination(*(volatile uint32_t *)&p); }
  178. void destination(volatile signed long &p) { destination(*(volatile uint32_t *)&p); }
  179. void destination(volatile unsigned long &p) {
  180. TCD->DADDR = &p;
  181. TCD->DOFF = 0;
  182. TCD->ATTR_DST = 2;
  183. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 4;
  184. TCD->DLASTSGA = 0;
  185. }
  186. // Use a buffer (array of data) as the data destination. Typically a
  187. // buffer for receiving data is used.
  188. void destinationBuffer(volatile signed char p[], unsigned int len) {
  189. destinationBuffer((volatile uint8_t *)p, len); }
  190. void destinationBuffer(volatile unsigned char p[], unsigned int len) {
  191. TCD->DADDR = p;
  192. TCD->DOFF = 1;
  193. TCD->ATTR_DST = 0;
  194. TCD->NBYTES = 1;
  195. TCD->DLASTSGA = -len;
  196. TCD->BITER = len;
  197. TCD->CITER = len;
  198. }
  199. void destinationBuffer(volatile signed short p[], unsigned int len) {
  200. destinationBuffer((volatile uint16_t *)p, len); }
  201. void destinationBuffer(volatile unsigned short p[], unsigned int len) {
  202. TCD->DADDR = p;
  203. TCD->DOFF = 2;
  204. TCD->ATTR_DST = 1;
  205. TCD->NBYTES = 2;
  206. TCD->DLASTSGA = -len;
  207. TCD->BITER = len / 2;
  208. TCD->CITER = len / 2;
  209. }
  210. void destinationBuffer(volatile signed int p[], unsigned int len) {
  211. destinationBuffer((volatile uint32_t *)p, len); }
  212. void destinationBuffer(volatile unsigned int p[], unsigned int len) {
  213. destinationBuffer((volatile uint32_t *)p, len); }
  214. void destinationBuffer(volatile signed long p[], unsigned int len) {
  215. destinationBuffer((volatile uint32_t *)p, len); }
  216. void destinationBuffer(volatile unsigned long p[], unsigned int len) {
  217. TCD->DADDR = p;
  218. TCD->DOFF = 4;
  219. TCD->ATTR_DST = 2;
  220. TCD->NBYTES = 4;
  221. TCD->DLASTSGA = -len;
  222. TCD->BITER = len / 4;
  223. TCD->CITER = len / 4;
  224. }
  225. // Use a circular buffer as the data destination
  226. void destinationCircular(volatile signed char p[], unsigned int len) {
  227. destinationCircular((volatile uint8_t *)p, len); }
  228. void destinationCircular(volatile unsigned char p[], unsigned int len) {
  229. TCD->DADDR = p;
  230. TCD->DOFF = 1;
  231. TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3);
  232. TCD->NBYTES = 1;
  233. TCD->DLASTSGA = 0;
  234. TCD->BITER = len;
  235. TCD->CITER = len;
  236. }
  237. void destinationCircular(volatile signed short p[], unsigned int len) {
  238. destinationCircular((volatile uint16_t *)p, len); }
  239. void destinationCircular(volatile unsigned short p[], unsigned int len) {
  240. TCD->DADDR = p;
  241. TCD->DOFF = 2;
  242. TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 1;
  243. TCD->NBYTES = 2;
  244. TCD->DLASTSGA = 0;
  245. TCD->BITER = len / 2;
  246. TCD->CITER = len / 2;
  247. }
  248. void destinationCircular(volatile signed int p[], unsigned int len) {
  249. destinationCircular((volatile uint32_t *)p, len); }
  250. void destinationCircular(volatile unsigned int p[], unsigned int len) {
  251. destinationCircular((volatile uint32_t *)p, len); }
  252. void destinationCircular(volatile signed long p[], unsigned int len) {
  253. destinationCircular((volatile uint32_t *)p, len); }
  254. void destinationCircular(volatile unsigned long p[], unsigned int len) {
  255. TCD->DADDR = p;
  256. TCD->DOFF = 4;
  257. TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 2;
  258. TCD->NBYTES = 4;
  259. TCD->DLASTSGA = 0;
  260. TCD->BITER = len / 4;
  261. TCD->CITER = len / 4;
  262. }
  263. /*************************************************/
  264. /** Quantity of Data to Transfer **/
  265. /*************************************************/
  266. // Set the data size used for each triggered transfer
  267. void transferSize(unsigned int len) {
  268. if (len == 16) {
  269. TCD->NBYTES = 16;
  270. if (TCD->SOFF != 0) TCD->SOFF = 16;
  271. if (TCD->DOFF != 0) TCD->DOFF = 16;
  272. TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0404;
  273. } else if (len == 4) {
  274. TCD->NBYTES = 4;
  275. if (TCD->SOFF != 0) TCD->SOFF = 4;
  276. if (TCD->DOFF != 0) TCD->DOFF = 4;
  277. TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0202;
  278. } else if (len == 2) {
  279. TCD->NBYTES = 2;
  280. if (TCD->SOFF != 0) TCD->SOFF = 2;
  281. if (TCD->DOFF != 0) TCD->DOFF = 2;
  282. TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0101;
  283. } else {
  284. TCD->NBYTES = 1;
  285. if (TCD->SOFF != 0) TCD->SOFF = 1;
  286. if (TCD->DOFF != 0) TCD->DOFF = 1;
  287. TCD->ATTR = TCD->ATTR & 0xF8F8;
  288. }
  289. }
  290. // Set the number of transfers (number of triggers until complete)
  291. void transferCount(unsigned int len) {
  292. if (!(TCD->BITER & DMA_TCD_BITER_ELINK)) {
  293. if (len > 32767) return;
  294. TCD->BITER = len;
  295. TCD->CITER = len;
  296. } else {
  297. if (len > 511) return;
  298. TCD->BITER = (TCD->BITER & 0xFE00) | len;
  299. TCD->CITER = (TCD->CITER & 0xFE00) | len;
  300. }
  301. }
  302. /*************************************************/
  303. /** Special Options / Features **/
  304. /*************************************************/
  305. void interruptAtCompletion(void) {
  306. TCD->CSR |= DMA_TCD_CSR_INTMAJOR;
  307. }
  308. void interruptAtHalf(void) {
  309. TCD->CSR |= DMA_TCD_CSR_INTHALF;
  310. }
  311. void disableOnCompletion(void) {
  312. TCD->CSR |= DMA_TCD_CSR_DREQ;
  313. }
  314. void replaceSettingsOnCompletion(const DMABaseClass &settings) {
  315. TCD->DLASTSGA = (int32_t)(settings.TCD);
  316. TCD->CSR &= ~DMA_TCD_CSR_DONE;
  317. TCD->CSR |= DMA_TCD_CSR_ESG;
  318. }
  319. protected:
  320. // users should not be able to create instances of DMABaseClass, which
  321. // require the inheriting class to initialize the TCD pointer.
  322. DMABaseClass() {}
  323. static inline void copy_tcd(TCD_t *dst, const TCD_t *src) {
  324. const uint32_t *p = (const uint32_t *)src;
  325. uint32_t *q = (uint32_t *)dst;
  326. uint32_t t1, t2, t3, t4;
  327. t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++;
  328. *q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4;
  329. t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++;
  330. *q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4;
  331. }
  332. };
  333. // DMASetting represents settings stored only in memory, which can be
  334. // applied to any DMA channel.
  335. class DMASetting : public DMABaseClass {
  336. public:
  337. DMASetting() {
  338. TCD = &tcddata;
  339. }
  340. DMASetting(const DMASetting &c) {
  341. TCD = &tcddata;
  342. *this = c;
  343. }
  344. DMASetting(const DMABaseClass &c) {
  345. TCD = &tcddata;
  346. *this = c;
  347. }
  348. DMASetting & operator = (const DMABaseClass &rhs) {
  349. copy_tcd(TCD, rhs.TCD);
  350. return *this;
  351. }
  352. private:
  353. TCD_t tcddata __attribute__((aligned(32)));
  354. };
  355. // DMAChannel reprents an actual DMA channel and its current settings
  356. class DMAChannel : public DMABaseClass {
  357. public:
  358. /*************************************************/
  359. /** Channel Allocation **/
  360. /*************************************************/
  361. DMAChannel() {
  362. begin();
  363. }
  364. DMAChannel(const DMAChannel &c) {
  365. TCD = c.TCD;
  366. channel = c.channel;
  367. }
  368. DMAChannel(const DMASetting &c) {
  369. begin();
  370. copy_tcd(TCD, c.TCD);
  371. }
  372. DMAChannel(bool allocate) {
  373. if (allocate) begin();
  374. }
  375. DMAChannel & operator = (const DMAChannel &rhs) {
  376. if (channel != rhs.channel) {
  377. release();
  378. TCD = rhs.TCD;
  379. channel = rhs.channel;
  380. }
  381. return *this;
  382. }
  383. DMAChannel & operator = (const DMASetting &rhs) {
  384. copy_tcd(TCD, rhs.TCD);
  385. return *this;
  386. }
  387. ~DMAChannel() {
  388. release();
  389. }
  390. void begin(bool force_initialization = false);
  391. private:
  392. void release(void);
  393. public:
  394. /***************************************/
  395. /** Triggering **/
  396. /***************************************/
  397. // Triggers cause the DMA channel to actually move data. Each
  398. // trigger moves a single data unit, which is typically 8, 16 or
  399. // 32 bits. If a channel is configured for 200 transfers
  400. // Use a hardware trigger to make the DMA channel run
  401. void triggerAtHardwareEvent(uint8_t source) {
  402. volatile uint8_t *mux;
  403. mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel;
  404. *mux = 0;
  405. *mux = (source & 63) | DMAMUX_ENABLE;
  406. }
  407. // Use another DMA channel as the trigger, causing this
  408. // channel to trigger after each transfer is makes, except
  409. // the its last transfer. This effectively makes the 2
  410. // channels run in parallel until the last transfer
  411. void triggerAtTransfersOf(DMABaseClass &ch) {
  412. ch.TCD->BITER = (ch.TCD->BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK)
  413. | DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK;
  414. ch.TCD->CITER = ch.TCD->BITER ;
  415. }
  416. // Use another DMA channel as the trigger, causing this
  417. // channel to trigger when the other channel completes.
  418. void triggerAtCompletionOf(DMABaseClass &ch) {
  419. ch.TCD->CSR = (ch.TCD->CSR & ~(DMA_TCD_CSR_MAJORLINKCH_MASK|DMA_TCD_CSR_DONE))
  420. | DMA_TCD_CSR_MAJORLINKCH(channel) | DMA_TCD_CSR_MAJORELINK;
  421. }
  422. // Cause this DMA channel to be continuously triggered, so
  423. // it will move data as rapidly as possible, without waiting.
  424. // Normally this would be used with disableOnCompletion().
  425. void triggerContinuously(void) {
  426. volatile uint8_t *mux = (volatile uint8_t *)&DMAMUX0_CHCFG0;
  427. mux[channel] = 0;
  428. #if DMAMUX_NUM_SOURCE_ALWAYS >= DMA_NUM_CHANNELS
  429. mux[channel] = DMAMUX_SOURCE_ALWAYS0 + channel;
  430. #else
  431. // search for an unused "always on" source
  432. unsigned int i = DMAMUX_SOURCE_ALWAYS0;
  433. for (i = DMAMUX_SOURCE_ALWAYS0;
  434. i < DMAMUX_SOURCE_ALWAYS0 + DMAMUX_NUM_SOURCE_ALWAYS; i++) {
  435. unsigned int ch;
  436. for (ch=0; ch < DMA_NUM_CHANNELS; ch++) {
  437. if (mux[ch] == i) break;
  438. }
  439. if (ch >= DMA_NUM_CHANNELS) {
  440. mux[channel] = (i | DMAMUX_ENABLE);
  441. return;
  442. }
  443. }
  444. #endif
  445. }
  446. // Manually trigger the DMA channel.
  447. void triggerManual(void) {
  448. DMA_SSRT = channel;
  449. }
  450. /***************************************/
  451. /** Interrupts **/
  452. /***************************************/
  453. // An interrupt routine can be run when the DMA channel completes
  454. // the entire transfer, and also optionally when half of the
  455. // transfer is completed.
  456. void attachInterrupt(void (*isr)(void)) {
  457. _VectorsRam[channel + IRQ_DMA_CH0 + 16] = isr;
  458. NVIC_ENABLE_IRQ(IRQ_DMA_CH0 + channel);
  459. }
  460. void detachInterrupt(void) {
  461. NVIC_DISABLE_IRQ(IRQ_DMA_CH0 + channel);
  462. }
  463. void clearInterrupt(void) {
  464. DMA_CINT = channel;
  465. }
  466. /***************************************/
  467. /** Enable / Disable **/
  468. /***************************************/
  469. void enable(void) {
  470. DMA_SERQ = channel;
  471. }
  472. void disable(void) {
  473. DMA_CERQ = channel;
  474. }
  475. /***************************************/
  476. /** Status **/
  477. /***************************************/
  478. bool complete(void) {
  479. if (TCD->CSR & DMA_TCD_CSR_DONE) return true;
  480. return false;
  481. }
  482. void clearComplete(void) {
  483. DMA_CDNE = channel;
  484. }
  485. bool error(void) {
  486. if (DMA_ERR & (1<<channel)) return true;
  487. return false;
  488. }
  489. void clearError(void) {
  490. DMA_CERR = channel;
  491. }
  492. void * sourceAddress(void) {
  493. return (void *)(TCD->SADDR);
  494. }
  495. void * destinationAddress(void) {
  496. return (void *)(TCD->DADDR);
  497. }
  498. /***************************************/
  499. /** Direct Hardware Access **/
  500. /***************************************/
  501. // For complex and unusual configurations not possible with the above
  502. // functions, the Transfer Control Descriptor (TCD) and channel number
  503. // can be used directly. This leads to less portable and less readable
  504. // code, but direct control of all parameters is possible.
  505. uint8_t channel;
  506. // TCD is accessible due to inheritance from DMABaseClass
  507. };
  508. // arrange the relative priority of 2 or more DMA channels
  509. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2);
  510. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3);
  511. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3, DMAChannel &ch4);
  512. /****************************************************************/
  513. /** Teensy-LC **/
  514. /****************************************************************/
  515. #elif defined(KINETISL)
  516. class DMABaseClass {
  517. public:
  518. typedef struct __attribute__((packed, aligned(4))) {
  519. volatile const void * volatile SAR;
  520. volatile void * volatile DAR;
  521. volatile uint32_t DSR_BCR;
  522. volatile uint32_t DCR;
  523. } CFG_t;
  524. CFG_t *CFG;
  525. /***************************************/
  526. /** Data Transfer **/
  527. /***************************************/
  528. // Use a single variable as the data source. Typically a register
  529. // for receiving data from one of the hardware peripherals is used.
  530. void source(volatile const signed char &p) { source(*(volatile const uint8_t *)&p); }
  531. void source(volatile const unsigned char &p) {
  532. CFG->SAR = &p;
  533. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(1);
  534. }
  535. void source(volatile const signed short &p) { source(*(volatile const uint16_t *)&p); }
  536. void source(volatile const unsigned short &p) {
  537. CFG->SAR = &p;
  538. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(2);
  539. }
  540. void source(volatile const signed int &p) { source(*(volatile const uint32_t *)&p); }
  541. void source(volatile const unsigned int &p) { source(*(volatile const uint32_t *)&p); }
  542. void source(volatile const signed long &p) { source(*(volatile const uint32_t *)&p); }
  543. void source(volatile const unsigned long &p) {
  544. CFG->SAR = &p;
  545. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(0);
  546. }
  547. // Use a buffer (array of data) as the data source. Typically a
  548. // buffer for transmitting data is used.
  549. void sourceBuffer(volatile const signed char p[], unsigned int len) {
  550. sourceBuffer((volatile const uint8_t *)p, len); }
  551. void sourceBuffer(volatile const unsigned char p[], unsigned int len) {
  552. if (len > 0xFFFFF) return;
  553. CFG->SAR = p;
  554. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(1) | DMA_DCR_SINC;
  555. CFG->DSR_BCR = len;
  556. }
  557. void sourceBuffer(volatile const signed short p[], unsigned int len) {
  558. sourceBuffer((volatile const uint16_t *)p, len); }
  559. void sourceBuffer(volatile const unsigned short p[], unsigned int len) {
  560. if (len > 0xFFFFF) return;
  561. CFG->SAR = p;
  562. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(2) | DMA_DCR_SINC;
  563. CFG->DSR_BCR = len;
  564. }
  565. void sourceBuffer(volatile const signed int p[], unsigned int len) {
  566. sourceBuffer((volatile const uint32_t *)p, len); }
  567. void sourceBuffer(volatile const unsigned int p[], unsigned int len) {
  568. sourceBuffer((volatile const uint32_t *)p, len); }
  569. void sourceBuffer(volatile const signed long p[], unsigned int len) {
  570. sourceBuffer((volatile const uint32_t *)p, len); }
  571. void sourceBuffer(volatile const unsigned long p[], unsigned int len) {
  572. if (len > 0xFFFFF) return;
  573. CFG->SAR = p;
  574. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(0) | DMA_DCR_SINC;
  575. CFG->DSR_BCR = len;
  576. }
  577. // Use a circular buffer as the data source
  578. void sourceCircular(volatile const signed char p[], unsigned int len) {
  579. sourceCircular((volatile const uint8_t *)p, len); }
  580. void sourceCircular(volatile const unsigned char p[], unsigned int len) {
  581. uint32_t mod = len2mod(len);
  582. if (mod == 0) return;
  583. CFG->SAR = p;
  584. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(1) | DMA_DCR_SINC
  585. | DMA_DCR_SMOD(mod);
  586. CFG->DSR_BCR = len;
  587. }
  588. void sourceCircular(volatile const signed short p[], unsigned int len) {
  589. sourceCircular((volatile const uint16_t *)p, len); }
  590. void sourceCircular(volatile const unsigned short p[], unsigned int len) {
  591. uint32_t mod = len2mod(len);
  592. if (mod == 0) return;
  593. CFG->SAR = p;
  594. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(2) | DMA_DCR_SINC
  595. | DMA_DCR_SMOD(mod);
  596. CFG->DSR_BCR = len;
  597. }
  598. void sourceCircular(volatile const signed int p[], unsigned int len) {
  599. sourceCircular((volatile const uint32_t *)p, len); }
  600. void sourceCircular(volatile const unsigned int p[], unsigned int len) {
  601. sourceCircular((volatile const uint32_t *)p, len); }
  602. void sourceCircular(volatile const signed long p[], unsigned int len) {
  603. sourceCircular((volatile const uint32_t *)p, len); }
  604. void sourceCircular(volatile const unsigned long p[], unsigned int len) {
  605. uint32_t mod = len2mod(len);
  606. if (mod == 0) return;
  607. CFG->SAR = p;
  608. CFG->DCR = (CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(0) | DMA_DCR_SINC
  609. | DMA_DCR_SMOD(mod);
  610. CFG->DSR_BCR = len;
  611. }
  612. // Use a single variable as the data destination. Typically a register
  613. // for transmitting data to one of the hardware peripherals is used.
  614. void destination(volatile signed char &p) { destination(*(volatile uint8_t *)&p); }
  615. void destination(volatile unsigned char &p) {
  616. CFG->DAR = &p;
  617. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(1);
  618. }
  619. void destination(volatile signed short &p) { destination(*(volatile uint16_t *)&p); }
  620. void destination(volatile unsigned short &p) {
  621. CFG->DAR = &p;
  622. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(2);
  623. }
  624. void destination(volatile signed int &p) { destination(*(volatile uint32_t *)&p); }
  625. void destination(volatile unsigned int &p) { destination(*(volatile uint32_t *)&p); }
  626. void destination(volatile signed long &p) { destination(*(volatile uint32_t *)&p); }
  627. void destination(volatile unsigned long &p) {
  628. CFG->DAR = &p;
  629. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(0);
  630. }
  631. // Use a buffer (array of data) as the data destination. Typically a
  632. // buffer for receiving data is used.
  633. void destinationBuffer(volatile signed char p[], unsigned int len) {
  634. destinationBuffer((volatile uint8_t *)p, len); }
  635. void destinationBuffer(volatile unsigned char p[], unsigned int len) {
  636. CFG->DAR = p;
  637. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(1) | DMA_DCR_DINC;
  638. CFG->DSR_BCR = len;
  639. }
  640. void destinationBuffer(volatile signed short p[], unsigned int len) {
  641. destinationBuffer((volatile uint16_t *)p, len); }
  642. void destinationBuffer(volatile unsigned short p[], unsigned int len) {
  643. CFG->DAR = p;
  644. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(2) | DMA_DCR_DINC;
  645. CFG->DSR_BCR = len;
  646. }
  647. void destinationBuffer(volatile signed int p[], unsigned int len) {
  648. destinationBuffer((volatile uint32_t *)p, len); }
  649. void destinationBuffer(volatile unsigned int p[], unsigned int len) {
  650. destinationBuffer((volatile uint32_t *)p, len); }
  651. void destinationBuffer(volatile signed long p[], unsigned int len) {
  652. destinationBuffer((volatile uint32_t *)p, len); }
  653. void destinationBuffer(volatile unsigned long p[], unsigned int len) {
  654. CFG->DAR = p;
  655. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(0) | DMA_DCR_DINC;
  656. CFG->DSR_BCR = len;
  657. }
  658. // Use a circular buffer as the data destination
  659. void destinationCircular(volatile signed char p[], unsigned int len) {
  660. destinationCircular((volatile uint8_t *)p, len); }
  661. void destinationCircular(volatile unsigned char p[], unsigned int len) {
  662. uint32_t mod = len2mod(len);
  663. if (mod == 0) return;
  664. CFG->DAR = p;
  665. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(1) | DMA_DCR_DINC
  666. | DMA_DCR_DMOD(mod);
  667. CFG->DSR_BCR = len;
  668. }
  669. void destinationCircular(volatile signed short p[], unsigned int len) {
  670. destinationCircular((volatile uint16_t *)p, len); }
  671. void destinationCircular(volatile unsigned short p[], unsigned int len) {
  672. uint32_t mod = len2mod(len);
  673. if (mod == 0) return;
  674. CFG->DAR = p;
  675. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(1) | DMA_DCR_DINC
  676. | DMA_DCR_DMOD(mod);
  677. CFG->DSR_BCR = len;
  678. }
  679. void destinationCircular(volatile signed int p[], unsigned int len) {
  680. destinationCircular((volatile uint32_t *)p, len); }
  681. void destinationCircular(volatile unsigned int p[], unsigned int len) {
  682. destinationCircular((volatile uint32_t *)p, len); }
  683. void destinationCircular(volatile signed long p[], unsigned int len) {
  684. destinationCircular((volatile uint32_t *)p, len); }
  685. void destinationCircular(volatile unsigned long p[], unsigned int len) {
  686. uint32_t mod = len2mod(len);
  687. if (mod == 0) return;
  688. CFG->DAR = p;
  689. CFG->DCR = (CFG->DCR & 0xF0F0F0FF) | DMA_DCR_DSIZE(1) | DMA_DCR_DINC
  690. | DMA_DCR_DMOD(mod);
  691. CFG->DSR_BCR = len;
  692. }
  693. /*************************************************/
  694. /** Quantity of Data to Transfer **/
  695. /*************************************************/
  696. // Set the data size used for each triggered transfer
  697. void transferSize(unsigned int len) {
  698. uint32_t dcr = CFG->DCR & 0xF0C8FFFF;
  699. if (len == 4) {
  700. CFG->DCR = dcr | DMA_DCR_DSIZE(0) | DMA_DCR_DSIZE(0);
  701. } else if (len == 2) {
  702. CFG->DCR = dcr | DMA_DCR_DSIZE(2) | DMA_DCR_DSIZE(2);
  703. } else {
  704. CFG->DCR = dcr | DMA_DCR_DSIZE(1) | DMA_DCR_DSIZE(1);
  705. }
  706. }
  707. // Set the number of transfers (number of triggers until complete)
  708. void transferCount(unsigned int len) {
  709. uint32_t s, d, n = 0; // 0 = 8 bit, 1 = 16 bit, 2 = 32 bit
  710. uint32_t dcr = CFG->DCR;
  711. s = (dcr >> 20) & 3;
  712. d = (dcr >> 17) & 3;
  713. if (s == 0 || d == 0) n = 2;
  714. else if (s == 2 || d == 2) n = 1;
  715. CFG->DSR_BCR = len << n;
  716. }
  717. /*************************************************/
  718. /** Special Options / Features **/
  719. /*************************************************/
  720. void interruptAtCompletion(void) {
  721. CFG->DCR |= DMA_DCR_EINT;
  722. }
  723. void disableOnCompletion(void) {
  724. CFG->DCR |= DMA_DCR_D_REQ;
  725. }
  726. // Kinetis-L DMA does not have these features :-(
  727. //
  728. // void interruptAtHalf(void) {}
  729. // void replaceSettingsOnCompletion(const DMABaseClass &settings) {};
  730. // TODO: can a 2nd linked channel be used to emulate this?
  731. protected:
  732. // users should not be able to create instances of DMABaseClass, which
  733. // require the inheriting class to initialize the TCD pointer.
  734. DMABaseClass() {}
  735. static inline void copy_cfg(CFG_t *dst, const CFG_t *src) {
  736. dst->SAR = src->SAR;
  737. dst->DAR = src->DAR;
  738. dst->DSR_BCR = src->DSR_BCR;
  739. dst->DCR = src->DCR;
  740. }
  741. private:
  742. static inline uint32_t len2mod(uint32_t len) {
  743. if (len < 16) return 0;
  744. if (len < 32) return 1;
  745. if (len < 64) return 2;
  746. if (len < 128) return 3;
  747. if (len < 256) return 4;
  748. if (len < 512) return 5;
  749. if (len < 1024) return 6;
  750. if (len < 2048) return 7;
  751. if (len < 4096) return 8;
  752. if (len < 8192) return 9;
  753. if (len < 16384) return 10;
  754. if (len < 32768) return 11;
  755. if (len < 65536) return 12;
  756. if (len < 131072) return 13;
  757. if (len < 262144) return 14;
  758. return 15;
  759. }
  760. };
  761. // DMASetting represents settings stored only in memory, which can be
  762. // applied to any DMA channel.
  763. class DMASetting : public DMABaseClass {
  764. public:
  765. DMASetting() {
  766. cfgdata.SAR = NULL;
  767. cfgdata.DAR = NULL;
  768. cfgdata.DSR_BCR = 0;
  769. cfgdata.DCR = DMA_DCR_CS;
  770. CFG = &cfgdata;
  771. }
  772. DMASetting(const DMASetting &c) {
  773. CFG = &cfgdata;
  774. *this = c;
  775. }
  776. DMASetting(const DMABaseClass &c) {
  777. CFG = &cfgdata;
  778. *this = c;
  779. }
  780. DMASetting & operator = (const DMABaseClass &rhs) {
  781. copy_cfg(CFG, rhs.CFG);
  782. return *this;
  783. }
  784. private:
  785. CFG_t cfgdata __attribute__((aligned(4)));
  786. };
  787. // DMAChannel reprents an actual DMA channel and its current settings
  788. class DMAChannel : public DMABaseClass {
  789. public:
  790. /*************************************************/
  791. /** Channel Allocation **/
  792. /*************************************************/
  793. DMAChannel() {
  794. begin();
  795. }
  796. DMAChannel(const DMAChannel &c) {
  797. CFG = c.CFG;
  798. channel = c.channel;
  799. }
  800. DMAChannel(const DMASetting &c) {
  801. begin();
  802. copy_cfg(CFG, c.CFG);
  803. }
  804. DMAChannel(bool allocate) {
  805. if (allocate) begin();
  806. }
  807. DMAChannel & operator = (const DMAChannel &rhs) {
  808. if (channel != rhs.channel) {
  809. release();
  810. CFG = rhs.CFG;
  811. channel = rhs.channel;
  812. }
  813. return *this;
  814. }
  815. DMAChannel & operator = (const DMASetting &rhs) {
  816. copy_cfg(CFG, rhs.CFG);
  817. return *this;
  818. }
  819. ~DMAChannel() {
  820. release();
  821. }
  822. void begin(bool force_initialization = false);
  823. private:
  824. void release(void);
  825. public:
  826. /***************************************/
  827. /** Triggering **/
  828. /***************************************/
  829. // Triggers cause the DMA channel to actually move data. Each
  830. // trigger moves a single data unit, which is typically 8, 16 or
  831. // 32 bits. If a channel is configured for 200 transfers
  832. // Use a hardware trigger to make the DMA channel run
  833. void triggerAtHardwareEvent(uint8_t source) {
  834. volatile uint8_t *mux;
  835. CFG->DCR |= DMA_DCR_CS;
  836. mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel;
  837. *mux = 0;
  838. *mux = (source & 63) | DMAMUX_ENABLE;
  839. }
  840. // Use another DMA channel as the trigger, causing this
  841. // channel to trigger after each transfer is makes, including
  842. // the its last transfer. This effectively makes the 2
  843. // channels run in parallel. Note, on Teensy 3.0 & 3.1,
  844. // this feature triggers on every transfer except the last.
  845. // On Teensy-LC, it triggers on every one, including the last.
  846. void triggerAtTransfersOf(DMABaseClass &ch) {
  847. uint32_t dcr = ch.CFG->DCR;
  848. uint32_t linkcc = (dcr >> 4) & 3;
  849. if (linkcc == 0 || linkcc == 2) {
  850. ch.CFG->DCR = (dcr & ~DMA_DCR_LCH1(3)) |
  851. DMA_DCR_LINKCC(2) | DMA_DCR_LCH1(channel);
  852. } else if (linkcc == 1) {
  853. ch.CFG->DCR = (dcr & ~DMA_DCR_LCH1(3)) |
  854. DMA_DCR_LCH1(channel);
  855. } else {
  856. uint32_t lch1 = (dcr >> 2) & 3;
  857. ch.CFG->DCR = (dcr
  858. & ~(DMA_DCR_LINKCC(3) | DMA_DCR_LCH2(3) | DMA_DCR_LCH1(3)))
  859. | DMA_DCR_LINKCC(1) | DMA_DCR_LCH2(lch1) | DMA_DCR_LCH1(channel);
  860. }
  861. }
  862. // Use another DMA channel as the trigger, causing this
  863. // channel to trigger when the other channel completes.
  864. void triggerAtCompletionOf(DMABaseClass &ch) {
  865. uint32_t dcr = ch.CFG->DCR;
  866. uint32_t linkcc = (dcr >> 4) & 3;
  867. if (linkcc == 0 || linkcc == 3) {
  868. ch.CFG->DCR = (dcr & ~DMA_DCR_LCH1(3)) |
  869. DMA_DCR_LINKCC(3) | DMA_DCR_LCH1(channel);
  870. } else {
  871. ch.CFG->DCR = (dcr
  872. & ~(DMA_DCR_LINKCC(3) | DMA_DCR_LCH2(3)))
  873. | DMA_DCR_LINKCC(1) | DMA_DCR_LCH2(channel);
  874. }
  875. }
  876. // Cause this DMA channel to be continuously triggered, so
  877. // it will move data as rapidly as possible, without waiting.
  878. // Normally this would be used with disableOnCompletion().
  879. void triggerContinuously(void) {
  880. uint32_t dcr = CFG->DCR;
  881. dcr &= ~(DMA_DCR_ERQ | DMA_DCR_CS);
  882. CFG->DCR = dcr;
  883. CFG->DCR = dcr | DMA_DCR_START;
  884. }
  885. // Manually trigger the DMA channel.
  886. void triggerManual(void) {
  887. CFG->DCR = (CFG->DCR & ~DMA_DCR_ERQ) | (DMA_DCR_CS | DMA_DCR_START);
  888. }
  889. /***************************************/
  890. /** Interrupts **/
  891. /***************************************/
  892. // An interrupt routine can be run when the DMA channel completes
  893. // the entire transfer, and also optionally when half of the
  894. // transfer is completed.
  895. void attachInterrupt(void (*isr)(void)) {
  896. _VectorsRam[channel + IRQ_DMA_CH0 + 16] = isr;
  897. NVIC_ENABLE_IRQ(IRQ_DMA_CH0 + channel);
  898. }
  899. void detachInterrupt(void) {
  900. NVIC_DISABLE_IRQ(IRQ_DMA_CH0 + channel);
  901. }
  902. void clearInterrupt(void) {
  903. CFG->DSR_BCR = DMA_DSR_BCR_DONE;
  904. }
  905. /***************************************/
  906. /** Enable / Disable **/
  907. /***************************************/
  908. void enable(void) {
  909. CFG->DCR |= DMA_DCR_ERQ;
  910. }
  911. void disable(void) {
  912. CFG->DCR &= ~DMA_DCR_ERQ;
  913. }
  914. /***************************************/
  915. /** Status **/
  916. /***************************************/
  917. bool complete(void) {
  918. if (CFG->DSR_BCR & DMA_DSR_BCR_DONE) return true;
  919. return false;
  920. }
  921. void clearComplete(void) {
  922. CFG->DSR_BCR |= DMA_DSR_BCR_DONE;
  923. }
  924. bool error(void) {
  925. if (CFG->DSR_BCR &
  926. (DMA_DSR_BCR_CE | DMA_DSR_BCR_BES | DMA_DSR_BCR_BED)) return true;
  927. return false;
  928. }
  929. void clearError(void) {
  930. CFG->DSR_BCR |= DMA_DSR_BCR_DONE;
  931. }
  932. void * sourceAddress(void) {
  933. return (void *)(CFG->SAR);
  934. }
  935. void * destinationAddress(void) {
  936. return (void *)(CFG->DAR);
  937. }
  938. /***************************************/
  939. /** Direct Hardware Access **/
  940. /***************************************/
  941. uint8_t channel;
  942. // CFG is accessible due to inheritance from DMABaseClass
  943. };
  944. // arrange the relative priority of 2 or more DMA channels
  945. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2);
  946. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3);
  947. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3, DMAChannel &ch4);
  948. #endif // KINETISL
  949. #endif // __cplusplus
  950. #endif // DMAChannel_h_