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.

преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. #ifndef DMAChannel_h_
  2. #define DMAChannel_h_
  3. #include "kinetis.h"
  4. // This code is a work-in-progress. It's incomplete and not usable yet...
  5. //
  6. // http://forum.pjrc.com/threads/25778-Could-there-be-something-like-an-ISR-template-function/page3
  7. // known libraries with DMA usage (in need of porting to this new scheme):
  8. //
  9. // https://github.com/PaulStoffregen/Audio
  10. // https://github.com/PaulStoffregen/OctoWS2811
  11. // https://github.com/pedvide/ADC
  12. // https://github.com/duff2013/SerialEvent
  13. // https://github.com/pixelmatix/SmartMatrix
  14. // https://github.com/crteensy/DmaSpi <-- DmaSpi has adopted this scheme
  15. #if defined(KINETISK)
  16. #ifdef __cplusplus
  17. #define DMACHANNEL_HAS_BEGIN
  18. #define DMACHANNEL_HAS_BOOLEAN_CTOR
  19. class DMABaseClass {
  20. public:
  21. typedef struct __attribute__((packed)) {
  22. volatile const void * volatile SADDR;
  23. int16_t SOFF;
  24. union { uint16_t ATTR;
  25. struct { uint8_t ATTR_DST; uint8_t ATTR_SRC; }; };
  26. union { uint32_t NBYTES; uint32_t NBYTES_MLNO;
  27. uint32_t NBYTES_MLOFFNO; uint32_t NBYTES_MLOFFYES; };
  28. int32_t SLAST;
  29. volatile void * volatile DADDR;
  30. int16_t DOFF;
  31. union { volatile uint16_t CITER;
  32. volatile uint16_t CITER_ELINKYES; volatile uint16_t CITER_ELINKNO; };
  33. int32_t DLASTSGA;
  34. volatile uint16_t CSR;
  35. union { volatile uint16_t BITER;
  36. volatile uint16_t BITER_ELINKYES; volatile uint16_t BITER_ELINKNO; };
  37. } TCD_t;
  38. TCD_t *TCD;
  39. /***************************************/
  40. /** Data Transfer **/
  41. /***************************************/
  42. // Use a single variable as the data source. Typically a register
  43. // for receiving data from one of the hardware peripherals is used.
  44. void source(volatile const signed char &p) { source(*(volatile const uint8_t *)&p); }
  45. void source(volatile const unsigned char &p) {
  46. TCD->SADDR = &p;
  47. TCD->SOFF = 0;
  48. TCD->ATTR_SRC = 0;
  49. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 1;
  50. TCD->SLAST = 0;
  51. }
  52. void source(volatile const signed short &p) { source(*(volatile const uint16_t *)&p); }
  53. void source(volatile const unsigned short &p) {
  54. TCD->SADDR = &p;
  55. TCD->SOFF = 0;
  56. TCD->ATTR_SRC = 1;
  57. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 2;
  58. TCD->SLAST = 0;
  59. }
  60. void source(volatile const signed int &p) { source(*(volatile const uint32_t *)&p); }
  61. void source(volatile const unsigned int &p) { source(*(volatile const uint32_t *)&p); }
  62. void source(volatile const signed long &p) { source(*(volatile const uint32_t *)&p); }
  63. void source(volatile const unsigned long &p) {
  64. TCD->SADDR = &p;
  65. TCD->SOFF = 0;
  66. TCD->ATTR_SRC = 2;
  67. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 4;
  68. TCD->SLAST = 0;
  69. }
  70. // Use a buffer (array of data) as the data source. Typically a
  71. // buffer for transmitting data is used.
  72. void sourceBuffer(volatile const signed char p[], unsigned int len) {
  73. sourceBuffer((volatile const uint8_t *)p, len); }
  74. void sourceBuffer(volatile const unsigned char p[], unsigned int len) {
  75. TCD->SADDR = p;
  76. TCD->SOFF = 1;
  77. TCD->ATTR_SRC = 0;
  78. TCD->NBYTES = 1;
  79. TCD->SLAST = -len;
  80. TCD->BITER = len;
  81. TCD->CITER = len;
  82. }
  83. void sourceBuffer(volatile const signed short p[], unsigned int len) {
  84. sourceBuffer((volatile const uint16_t *)p, len); }
  85. void sourceBuffer(volatile const unsigned short p[], unsigned int len) {
  86. TCD->SADDR = p;
  87. TCD->SOFF = 2;
  88. TCD->ATTR_SRC = 1;
  89. TCD->NBYTES = 2;
  90. TCD->SLAST = -len;
  91. TCD->BITER = len / 2;
  92. TCD->CITER = len / 2;
  93. }
  94. void sourceBuffer(volatile const signed int p[], unsigned int len) {
  95. sourceBuffer((volatile const uint32_t *)p, len); }
  96. void sourceBuffer(volatile const unsigned int p[], unsigned int len) {
  97. sourceBuffer((volatile const uint32_t *)p, len); }
  98. void sourceBuffer(volatile const signed long p[], unsigned int len) {
  99. sourceBuffer((volatile const uint32_t *)p, len); }
  100. void sourceBuffer(volatile const unsigned long p[], unsigned int len) {
  101. TCD->SADDR = p;
  102. TCD->SOFF = 4;
  103. TCD->ATTR_SRC = 2;
  104. TCD->NBYTES = 4;
  105. TCD->SLAST = -len;
  106. TCD->BITER = len / 4;
  107. TCD->CITER = len / 4;
  108. }
  109. // Use a circular buffer as the data source
  110. void sourceCircular(volatile const signed char p[], unsigned int len) {
  111. sourceCircular((volatile const uint8_t *)p, len); }
  112. void sourceCircular(volatile const unsigned char p[], unsigned int len) {
  113. TCD->SADDR = p;
  114. TCD->SOFF = 1;
  115. TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3);
  116. TCD->NBYTES = 1;
  117. TCD->SLAST = 0;
  118. TCD->BITER = len;
  119. TCD->CITER = len;
  120. }
  121. void sourceCircular(volatile const signed short p[], unsigned int len) {
  122. sourceCircular((volatile const uint16_t *)p, len); }
  123. void sourceCircular(volatile const unsigned short p[], unsigned int len) {
  124. TCD->SADDR = p;
  125. TCD->SOFF = 2;
  126. TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 1;
  127. TCD->NBYTES = 2;
  128. TCD->SLAST = 0;
  129. TCD->BITER = len / 2;
  130. TCD->CITER = len / 2;
  131. }
  132. void sourceCircular(volatile const signed int p[], unsigned int len) {
  133. sourceCircular((volatile const uint32_t *)p, len); }
  134. void sourceCircular(volatile const unsigned int p[], unsigned int len) {
  135. sourceCircular((volatile const uint32_t *)p, len); }
  136. void sourceCircular(volatile const signed long p[], unsigned int len) {
  137. sourceCircular((volatile const uint32_t *)p, len); }
  138. void sourceCircular(volatile const unsigned long p[], unsigned int len) {
  139. TCD->SADDR = p;
  140. TCD->SOFF = 4;
  141. TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 2;
  142. TCD->NBYTES = 4;
  143. TCD->SLAST = 0;
  144. TCD->BITER = len / 4;
  145. TCD->CITER = len / 4;
  146. }
  147. // Use a single variable as the data destination. Typically a register
  148. // for transmitting data to one of the hardware peripherals is used.
  149. void destination(volatile signed char &p) { destination(*(volatile uint8_t *)&p); }
  150. void destination(volatile unsigned char &p) {
  151. TCD->DADDR = &p;
  152. TCD->DOFF = 0;
  153. TCD->ATTR_DST = 0;
  154. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 1;
  155. TCD->DLASTSGA = 0;
  156. }
  157. void destination(volatile signed short &p) { destination(*(volatile uint16_t *)&p); }
  158. void destination(volatile unsigned short &p) {
  159. TCD->DADDR = &p;
  160. TCD->DOFF = 0;
  161. TCD->ATTR_DST = 1;
  162. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 2;
  163. TCD->DLASTSGA = 0;
  164. }
  165. void destination(volatile signed int &p) { destination(*(volatile uint32_t *)&p); }
  166. void destination(volatile unsigned int &p) { destination(*(volatile uint32_t *)&p); }
  167. void destination(volatile signed long &p) { destination(*(volatile uint32_t *)&p); }
  168. void destination(volatile unsigned long &p) {
  169. TCD->DADDR = &p;
  170. TCD->DOFF = 0;
  171. TCD->ATTR_DST = 2;
  172. if ((uint32_t)&p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 4;
  173. TCD->DLASTSGA = 0;
  174. }
  175. // Use a buffer (array of data) as the data destination. Typically a
  176. // buffer for receiving data is used.
  177. void destinationBuffer(volatile signed char p[], unsigned int len) {
  178. destinationBuffer((volatile uint8_t *)p, len); }
  179. void destinationBuffer(volatile unsigned char p[], unsigned int len) {
  180. TCD->DADDR = p;
  181. TCD->DOFF = 1;
  182. TCD->ATTR_DST = 0;
  183. TCD->NBYTES = 1;
  184. TCD->DLASTSGA = -len;
  185. TCD->BITER = len;
  186. TCD->CITER = len;
  187. }
  188. void destinationBuffer(volatile signed short p[], unsigned int len) {
  189. destinationBuffer((volatile uint16_t *)p, len); }
  190. void destinationBuffer(volatile unsigned short p[], unsigned int len) {
  191. TCD->DADDR = p;
  192. TCD->DOFF = 2;
  193. TCD->ATTR_DST = 1;
  194. TCD->NBYTES = 2;
  195. TCD->DLASTSGA = -len;
  196. TCD->BITER = len / 2;
  197. TCD->CITER = len / 2;
  198. }
  199. void destinationBuffer(volatile signed int p[], unsigned int len) {
  200. destinationBuffer((volatile uint32_t *)p, len); }
  201. void destinationBuffer(volatile unsigned int p[], unsigned int len) {
  202. destinationBuffer((volatile uint32_t *)p, len); }
  203. void destinationBuffer(volatile signed long p[], unsigned int len) {
  204. destinationBuffer((volatile uint32_t *)p, len); }
  205. void destinationBuffer(volatile unsigned long p[], unsigned int len) {
  206. TCD->DADDR = p;
  207. TCD->DOFF = 4;
  208. TCD->ATTR_DST = 2;
  209. TCD->NBYTES = 4;
  210. TCD->DLASTSGA = -len;
  211. TCD->BITER = len / 4;
  212. TCD->CITER = len / 4;
  213. }
  214. // Use a circular buffer as the data destination
  215. void destinationCircular(volatile signed char p[], unsigned int len) {
  216. destinationCircular((volatile uint8_t *)p, len); }
  217. void destinationCircular(volatile unsigned char p[], unsigned int len) {
  218. TCD->DADDR = p;
  219. TCD->DOFF = 1;
  220. TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3);
  221. TCD->NBYTES = 1;
  222. TCD->DLASTSGA = 0;
  223. TCD->BITER = len;
  224. TCD->CITER = len;
  225. }
  226. void destinationCircular(volatile signed short p[], unsigned int len) {
  227. destinationCircular((volatile uint16_t *)p, len); }
  228. void destinationCircular(volatile unsigned short p[], unsigned int len) {
  229. TCD->DADDR = p;
  230. TCD->DOFF = 2;
  231. TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 1;
  232. TCD->NBYTES = 2;
  233. TCD->DLASTSGA = 0;
  234. TCD->BITER = len / 2;
  235. TCD->CITER = len / 2;
  236. }
  237. void destinationCircular(volatile signed int p[], unsigned int len) {
  238. destinationCircular((volatile uint32_t *)p, len); }
  239. void destinationCircular(volatile unsigned int p[], unsigned int len) {
  240. destinationCircular((volatile uint32_t *)p, len); }
  241. void destinationCircular(volatile signed long p[], unsigned int len) {
  242. destinationCircular((volatile uint32_t *)p, len); }
  243. void destinationCircular(volatile unsigned long p[], unsigned int len) {
  244. TCD->DADDR = p;
  245. TCD->DOFF = 4;
  246. TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 2;
  247. TCD->NBYTES = 4;
  248. TCD->DLASTSGA = 0;
  249. TCD->BITER = len / 4;
  250. TCD->CITER = len / 4;
  251. }
  252. /*************************************************/
  253. /** Quantity of Data to Transfer **/
  254. /*************************************************/
  255. // Set the data size used for each triggered transfer
  256. void transferSize(unsigned int len) {
  257. if (len == 4) {
  258. TCD->NBYTES = 4;
  259. if (TCD->SOFF != 0) TCD->SOFF = 4;
  260. if (TCD->DOFF != 0) TCD->DOFF = 4;
  261. TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0202;
  262. } else if (len == 2) {
  263. TCD->NBYTES = 2;
  264. if (TCD->SOFF != 0) TCD->SOFF = 2;
  265. if (TCD->DOFF != 0) TCD->DOFF = 2;
  266. TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0101;
  267. } else {
  268. TCD->NBYTES = 1;
  269. if (TCD->SOFF != 0) TCD->SOFF = 1;
  270. if (TCD->DOFF != 0) TCD->DOFF = 1;
  271. TCD->ATTR = TCD->ATTR & 0xF8F8;
  272. }
  273. }
  274. // Set the number of transfers (number of triggers until complete)
  275. void transferCount(unsigned int len) {
  276. if (len > 32767) return;
  277. if (len >= 512) {
  278. TCD->BITER = len;
  279. TCD->CITER = len;
  280. } else {
  281. TCD->BITER = (TCD->BITER & 0xFE00) | len;
  282. TCD->CITER = (TCD->CITER & 0xFE00) | len;
  283. }
  284. }
  285. /*************************************************/
  286. /** Special Options / Features **/
  287. /*************************************************/
  288. void interruptAtCompletion(void) {
  289. TCD->CSR |= DMA_TCD_CSR_INTMAJOR;
  290. }
  291. void interruptAtHalf(void) {
  292. TCD->CSR |= DMA_TCD_CSR_INTHALF;
  293. }
  294. void disableOnCompletion(void) {
  295. TCD->CSR |= DMA_TCD_CSR_DREQ;
  296. }
  297. void replaceSettingsOnCompletion(const DMABaseClass &settings) {
  298. TCD->DLASTSGA = (int32_t)(settings.TCD);
  299. TCD->CSR &= ~DMA_TCD_CSR_DONE;
  300. TCD->CSR |= DMA_TCD_CSR_ESG;
  301. }
  302. protected:
  303. // users should not be able to create instances of DMABaseClass, which
  304. // require the inheriting class to initialize the TCD pointer.
  305. DMABaseClass() {}
  306. static inline void copy_tcd(TCD_t *dst, const TCD_t *src) {
  307. const uint32_t *p = (const uint32_t *)src;
  308. uint32_t *q = (uint32_t *)dst;
  309. uint32_t t1, t2, t3, t4;
  310. t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++;
  311. *q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4;
  312. t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++;
  313. *q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4;
  314. }
  315. };
  316. // DMASetting represents settings stored only in memory, which can be
  317. // applied to any DMA channel.
  318. class DMASetting : public DMABaseClass {
  319. public:
  320. DMASetting() {
  321. TCD = &tcddata;
  322. }
  323. DMASetting(const DMASetting &c) {
  324. TCD = &tcddata;
  325. *this = c;
  326. }
  327. DMASetting(const DMABaseClass &c) {
  328. TCD = &tcddata;
  329. *this = c;
  330. }
  331. DMASetting & operator = (const DMABaseClass &rhs) {
  332. copy_tcd(TCD, rhs.TCD);
  333. return *this;
  334. }
  335. private:
  336. TCD_t tcddata __attribute__((aligned(32)));
  337. };
  338. // DMAChannel reprents an actual DMA channel and its current settings
  339. class DMAChannel : public DMABaseClass {
  340. public:
  341. /*************************************************/
  342. /** Channel Allocation **/
  343. /*************************************************/
  344. DMAChannel() {
  345. begin();
  346. }
  347. DMAChannel(const DMAChannel &c) {
  348. TCD = c.TCD;
  349. channel = c.channel;
  350. }
  351. DMAChannel(const DMASetting &c) {
  352. begin();
  353. copy_tcd(TCD, c.TCD);
  354. }
  355. DMAChannel(bool allocate) {
  356. if (allocate) begin();
  357. }
  358. DMAChannel & operator = (const DMAChannel &rhs) {
  359. if (channel != rhs.channel) {
  360. release();
  361. TCD = rhs.TCD;
  362. channel = rhs.channel;
  363. }
  364. return *this;
  365. }
  366. DMAChannel & operator = (const DMASetting &rhs) {
  367. copy_tcd(TCD, rhs.TCD);
  368. return *this;
  369. }
  370. ~DMAChannel() {
  371. release();
  372. }
  373. void begin(bool force_initialization = false);
  374. private:
  375. void release(void);
  376. public:
  377. /***************************************/
  378. /** Triggering **/
  379. /***************************************/
  380. // Triggers cause the DMA channel to actually move data. Each
  381. // trigger moves a single data unit, which is typically 8, 16 or
  382. // 32 bits. If a channel is configured for 200 transfers
  383. // Use a hardware trigger to make the DMA channel run
  384. void triggerAtHardwareEvent(uint8_t source) {
  385. volatile uint8_t *mux;
  386. mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel;
  387. *mux = 0;
  388. *mux = (source & 63) | DMAMUX_ENABLE;
  389. }
  390. // Use another DMA channel as the trigger, causing this
  391. // channel to trigger after each transfer is makes, except
  392. // the its last transfer. This effectively makes the 2
  393. // channels run in parallel until the last transfer
  394. void triggerAtTransfersOf(DMABaseClass &ch) {
  395. ch.TCD->BITER = (ch.TCD->BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK)
  396. | DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK;
  397. ch.TCD->CITER = ch.TCD->BITER ;
  398. }
  399. // Use another DMA channel as the trigger, causing this
  400. // channel to trigger when the other channel completes.
  401. void triggerAtCompletionOf(DMABaseClass &ch) {
  402. ch.TCD->CSR = (ch.TCD->CSR & ~(DMA_TCD_CSR_MAJORLINKCH_MASK|DMA_TCD_CSR_DONE))
  403. | DMA_TCD_CSR_MAJORLINKCH(channel) | DMA_TCD_CSR_MAJORELINK;
  404. }
  405. // Cause this DMA channel to be continuously triggered, so
  406. // it will move data as rapidly as possible, without waiting.
  407. // Normally this would be used with disableOnCompletion().
  408. void triggerContinuously(void) {
  409. volatile uint8_t *mux = (volatile uint8_t *)&DMAMUX0_CHCFG0;
  410. mux[channel] = 0;
  411. #if DMAMUX_NUM_SOURCE_ALWAYS >= DMA_NUM_CHANNELS
  412. mux[channel] = DMAMUX_SOURCE_ALWAYS0 + channel;
  413. #else
  414. // search for an unused "always on" source
  415. unsigned int i = DMAMUX_SOURCE_ALWAYS0;
  416. for (i = DMAMUX_SOURCE_ALWAYS0;
  417. i < DMAMUX_SOURCE_ALWAYS0 + DMAMUX_NUM_SOURCE_ALWAYS; i++) {
  418. unsigned int ch;
  419. for (ch=0; ch < DMA_NUM_CHANNELS; ch++) {
  420. if (mux[ch] == i) break;
  421. }
  422. if (ch >= DMA_NUM_CHANNELS) {
  423. mux[channel] = (i | DMAMUX_ENABLE);
  424. return;
  425. }
  426. }
  427. #endif
  428. }
  429. // Manually trigger the DMA channel.
  430. void triggerManual(void) {
  431. DMA_SSRT = channel;
  432. }
  433. /***************************************/
  434. /** Interrupts **/
  435. /***************************************/
  436. // An interrupt routine can be run when the DMA channel completes
  437. // the entire transfer, and also optionally when half of the
  438. // transfer is completed.
  439. void attachInterrupt(void (*isr)(void)) {
  440. _VectorsRam[channel + IRQ_DMA_CH0 + 16] = isr;
  441. NVIC_ENABLE_IRQ(IRQ_DMA_CH0 + channel);
  442. }
  443. void detachInterrupt(void) {
  444. NVIC_DISABLE_IRQ(IRQ_DMA_CH0 + channel);
  445. }
  446. void clearInterrupt(void) {
  447. DMA_CINT = channel;
  448. }
  449. /***************************************/
  450. /** Enable / Disable **/
  451. /***************************************/
  452. void enable(void) {
  453. DMA_SERQ = channel;
  454. }
  455. void disable(void) {
  456. DMA_CERQ = channel;
  457. }
  458. /***************************************/
  459. /** Status **/
  460. /***************************************/
  461. bool complete(void) {
  462. if (TCD->CSR & DMA_TCD_CSR_DONE) return true;
  463. return false;
  464. }
  465. void clearComplete(void) {
  466. DMA_CDNE = channel;
  467. }
  468. bool error(void) {
  469. if (DMA_ERR & (1<<channel)) return true;
  470. return false;
  471. }
  472. void clearError(void) {
  473. DMA_CERR = channel;
  474. }
  475. void * sourceAddress(void) {
  476. return (void *)(TCD->SADDR);
  477. }
  478. void * destinationAddress(void) {
  479. return (void *)(TCD->DADDR);
  480. }
  481. /***************************************/
  482. /** Direct Hardware Access **/
  483. /***************************************/
  484. // For complex and unusual configurations not possible with the above
  485. // functions, the Transfer Control Descriptor (TCD) and channel number
  486. // can be used directly. This leads to less portable and less readable
  487. // code, but direct control of all parameters is possible.
  488. uint8_t channel;
  489. // TCD is accessible due to inheritance from DMABaseClass
  490. /* usage cases:
  491. ************************
  492. OctoWS2811:
  493. ************************
  494. // enable clocks to the DMA controller and DMAMUX
  495. SIM_SCGC7 |= SIM_SCGC7_DMA;
  496. SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
  497. DMA_CR = 0;
  498. DMA_CERQ = 1;
  499. DMA_CERQ = 2;
  500. DMA_CERQ = 3;
  501. // DMA channel #1 sets WS2811 high at the beginning of each cycle
  502. DMA_TCD1_SADDR = &ones;
  503. DMA_TCD1_SOFF = 0;
  504. DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
  505. DMA_TCD1_NBYTES_MLNO = 1;
  506. DMA_TCD1_SLAST = 0;
  507. DMA_TCD1_DADDR = &GPIOD_PSOR;
  508. DMA_TCD1_DOFF = 0;
  509. DMA_TCD1_CITER_ELINKNO = bufsize;
  510. DMA_TCD1_DLASTSGA = 0;
  511. DMA_TCD1_CSR = DMA_TCD_CSR_DREQ;
  512. DMA_TCD1_BITER_ELINKNO = bufsize;
  513. dma1.source(ones);
  514. dma1.destination(GPIOD_PSOR);
  515. dma1.size(1);
  516. dma1.count(bufsize);
  517. dma1.disableOnCompletion();
  518. // DMA channel #2 writes the pixel data at 20% of the cycle
  519. DMA_TCD2_SADDR = frameBuffer;
  520. DMA_TCD2_SOFF = 1;
  521. DMA_TCD2_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
  522. DMA_TCD2_NBYTES_MLNO = 1;
  523. DMA_TCD2_SLAST = -bufsize;
  524. DMA_TCD2_DADDR = &GPIOD_PDOR;
  525. DMA_TCD2_DOFF = 0;
  526. DMA_TCD2_CITER_ELINKNO = bufsize;
  527. DMA_TCD2_DLASTSGA = 0;
  528. DMA_TCD2_CSR = DMA_TCD_CSR_DREQ;
  529. DMA_TCD2_BITER_ELINKNO = bufsize;
  530. dma2.source(frameBuffer, sizeof(frameBuffer));
  531. dma2.destination(GPIOD_PDOR);
  532. dma2.size(1);
  533. dma2.count(bufsize);
  534. dma2.disableOnCompletion();
  535. // DMA channel #3 clear all the pins low at 48% of the cycle
  536. DMA_TCD3_SADDR = &ones;
  537. DMA_TCD3_SOFF = 0;
  538. DMA_TCD3_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
  539. DMA_TCD3_NBYTES_MLNO = 1;
  540. DMA_TCD3_SLAST = 0;
  541. DMA_TCD3_DADDR = &GPIOD_PCOR;
  542. DMA_TCD3_DOFF = 0;
  543. DMA_TCD3_CITER_ELINKNO = bufsize;
  544. DMA_TCD3_DLASTSGA = 0;
  545. DMA_TCD3_CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR;
  546. DMA_TCD3_BITER_ELINKNO = bufsize;
  547. dma3.source(ones);
  548. dma3.destination(GPIOD_PCOR);
  549. dma3.size(1);
  550. dma3.count(bufsize);
  551. dma3.disableOnCompletion();
  552. ************************
  553. Audio, DAC
  554. ************************
  555. DMA_CR = 0;
  556. DMA_TCD4_SADDR = dac_buffer;
  557. DMA_TCD4_SOFF = 2;
  558. DMA_TCD4_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  559. DMA_TCD4_NBYTES_MLNO = 2;
  560. DMA_TCD4_SLAST = -sizeof(dac_buffer);
  561. DMA_TCD4_DADDR = &DAC0_DAT0L;
  562. DMA_TCD4_DOFF = 0;
  563. DMA_TCD4_CITER_ELINKNO = sizeof(dac_buffer) / 2;
  564. DMA_TCD4_DLASTSGA = 0;
  565. DMA_TCD4_BITER_ELINKNO = sizeof(dac_buffer) / 2;
  566. DMA_TCD4_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  567. DMAMUX0_CHCFG4 = DMAMUX_DISABLE;
  568. DMAMUX0_CHCFG4 = DMAMUX_SOURCE_PDB | DMAMUX_ENABLE;
  569. ************************
  570. Audio, I2S
  571. ************************
  572. DMA_CR = 0;
  573. DMA_TCD0_SADDR = i2s_tx_buffer;
  574. DMA_TCD0_SOFF = 2;
  575. DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  576. DMA_TCD0_NBYTES_MLNO = 2;
  577. DMA_TCD0_SLAST = -sizeof(i2s_tx_buffer);
  578. DMA_TCD0_DADDR = &I2S0_TDR0;
  579. DMA_TCD0_DOFF = 0;
  580. DMA_TCD0_CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
  581. DMA_TCD0_DLASTSGA = 0;
  582. DMA_TCD0_BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
  583. DMA_TCD0_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  584. DMAMUX0_CHCFG0 = DMAMUX_DISABLE;
  585. DMAMUX0_CHCFG0 = DMAMUX_SOURCE_I2S0_TX | DMAMUX_ENABLE;
  586. ************************
  587. ADC lib, Pedro Villanueva
  588. ************************
  589. DMA_CR = 0; // normal mode of operation
  590. *DMAMUX0_CHCFG = DMAMUX_DISABLE; // disable before changing
  591. *DMA_TCD_ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_16BIT) |
  592. DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_16BIT) |
  593. DMA_TCD_ATTR_DMOD(4); // src and dst data is 16 bit (2 bytes), buffer size 2^^4 bytes = 8 values
  594. *DMA_TCD_NBYTES_MLNO = 2; // Minor Byte Transfer Count 2 bytes = 16 bits (we transfer 2 bytes each minor loop)
  595. *DMA_TCD_SADDR = ADC_RA; // source address
  596. *DMA_TCD_SOFF = 0; // don't change the address when minor loop finishes
  597. *DMA_TCD_SLAST = 0; // don't change src address after major loop completes
  598. *DMA_TCD_DADDR = elems; // destination address
  599. *DMA_TCD_DOFF = 2; // increment 2 bytes each minor loop
  600. *DMA_TCD_DLASTSGA = 0; // modulus feature takes care of going back to first element
  601. *DMA_TCD_CITER_ELINKNO = 1; // Current Major Iteration Count with channel linking disabled
  602. *DMA_TCD_BITER_ELINKNO = 1; // Starting Major Iteration Count with channel linking disabled
  603. *DMA_TCD_CSR = DMA_TCD_CSR_INTMAJOR; // Control and status: interrupt when major counter is complete
  604. DMA_CERQ = DMA_CERQ_CERQ(DMA_channel); // clear all past request
  605. DMA_CINT = DMA_channel; // clear interrupts
  606. uint8_t DMAMUX_SOURCE_ADC = DMAMUX_SOURCE_ADC0;
  607. if(ADC_number==1){
  608. DMAMUX_SOURCE_ADC = DMAMUX_SOURCE_ADC1;
  609. }
  610. *DMAMUX0_CHCFG = DMAMUX_SOURCE_ADC | DMAMUX_ENABLE; // enable mux and set channel DMA_channel to ADC0
  611. DMA_SERQ = DMA_SERQ_SERQ(DMA_channel); // enable DMA request
  612. NVIC_ENABLE_IRQ(IRQ_DMA_CH); // enable interrupts
  613. ************************
  614. SmartMatrix
  615. ************************
  616. // enable minor loop mapping so addresses can get reset after minor loops
  617. DMA_CR = 1 << 7;
  618. // DMA channel #0 - on latch rising edge, read address from fixed address temporary buffer, and output address on GPIO
  619. // using combo of writes to set+clear registers, to only modify the address pins and not other GPIO pins
  620. // address temporary buffer is refreshed before each DMA trigger (by DMA channel #2)
  621. // only use single major loop, never disable channel
  622. #define ADDRESS_ARRAY_REGISTERS_TO_UPDATE 2
  623. DMA_TCD0_SADDR = &gpiosync.gpio_pcor;
  624. DMA_TCD0_SOFF = (int)&gpiosync.gpio_psor - (int)&gpiosync.gpio_pcor;
  625. DMA_TCD0_SLAST = (ADDRESS_ARRAY_REGISTERS_TO_UPDATE * ((int)&ADDX_GPIO_CLEAR_REGISTER - (int)&ADDX_GPIO_SET_REGISTER));
  626. DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  627. // Destination Minor Loop Offset Enabled - transfer appropriate number of bytes per minor loop, and put DADDR back to original value when minor loop is complete
  628. // Source Minor Loop Offset Enabled - source buffer is same size and offset as destination so values reset after each minor loop
  629. DMA_TCD0_NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE | DMA_TCD_NBYTES_DMLOE |
  630. ((ADDRESS_ARRAY_REGISTERS_TO_UPDATE * ((int)&ADDX_GPIO_CLEAR_REGISTER - (int)&ADDX_GPIO_SET_REGISTER)) << 10) |
  631. (ADDRESS_ARRAY_REGISTERS_TO_UPDATE * sizeof(gpiosync.gpio_psor));
  632. // start on higher value of two registers, and make offset decrement to avoid negative number in NBYTES_MLOFFYES (TODO: can switch order by masking negative offset)
  633. DMA_TCD0_DADDR = &ADDX_GPIO_CLEAR_REGISTER;
  634. // update destination address so the second update per minor loop is ADDX_GPIO_SET_REGISTER
  635. DMA_TCD0_DOFF = (int)&ADDX_GPIO_SET_REGISTER - (int)&ADDX_GPIO_CLEAR_REGISTER;
  636. DMA_TCD0_DLASTSGA = (ADDRESS_ARRAY_REGISTERS_TO_UPDATE * ((int)&ADDX_GPIO_CLEAR_REGISTER - (int)&ADDX_GPIO_SET_REGISTER));
  637. // single major loop
  638. DMA_TCD0_CITER_ELINKNO = 1;
  639. DMA_TCD0_BITER_ELINKNO = 1;
  640. // link channel 1, enable major channel-to-channel linking, don't clear enable on major loop complete
  641. DMA_TCD0_CSR = (1 << 8) | (1 << 5);
  642. DMAMUX0_CHCFG0 = DMAMUX_SOURCE_LATCH_RISING_EDGE | DMAMUX_ENABLE;
  643. // DMA channel #1 - copy address values from current position in array to buffer to temporarily hold row values for the next timer cycle
  644. // only use single major loop, never disable channel
  645. DMA_TCD1_SADDR = &matrixUpdateBlocks[0][0].addressValues;
  646. DMA_TCD1_SOFF = sizeof(uint16_t);
  647. DMA_TCD1_SLAST = sizeof(matrixUpdateBlock) - (ADDRESS_ARRAY_REGISTERS_TO_UPDATE * sizeof(uint16_t));
  648. DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  649. // 16-bit = 2 bytes transferred
  650. // transfer two 16-bit values, reset destination address back after each minor loop
  651. DMA_TCD1_NBYTES_MLOFFNO = (ADDRESS_ARRAY_REGISTERS_TO_UPDATE * sizeof(uint16_t));
  652. // start with the register that's the highest location in memory and make offset decrement to avoid negative number in NBYTES_MLOFFYES register (TODO: can switch order by masking negative offset)
  653. DMA_TCD1_DADDR = &gpiosync.gpio_pcor;
  654. DMA_TCD1_DOFF = (int)&gpiosync.gpio_psor - (int)&gpiosync.gpio_pcor;
  655. DMA_TCD1_DLASTSGA = (ADDRESS_ARRAY_REGISTERS_TO_UPDATE * ((int)&gpiosync.gpio_pcor - (int)&gpiosync.gpio_psor));
  656. // no minor loop linking, single major loop, single minor loop, don't clear enable after major loop complete
  657. DMA_TCD1_CITER_ELINKNO = 1;
  658. DMA_TCD1_BITER_ELINKNO = 1;
  659. DMA_TCD1_CSR = 0;
  660. // DMA channel #2 - on latch falling edge, load FTM1_CV1 and FTM1_MOD with with next values from current block
  661. // only use single major loop, never disable channel
  662. // link to channel 3 when complete
  663. #define TIMER_REGISTERS_TO_UPDATE 2
  664. DMA_TCD2_SADDR = &matrixUpdateBlocks[0][0].timerValues.timer_oe;
  665. DMA_TCD2_SOFF = sizeof(uint16_t);
  666. DMA_TCD2_SLAST = sizeof(matrixUpdateBlock) - (TIMER_REGISTERS_TO_UPDATE * sizeof(uint16_t));
  667. DMA_TCD2_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  668. // 16-bit = 2 bytes transferred
  669. DMA_TCD2_NBYTES_MLOFFNO = TIMER_REGISTERS_TO_UPDATE * sizeof(uint16_t);
  670. DMA_TCD2_DADDR = &FTM1_C1V;
  671. DMA_TCD2_DOFF = (int)&FTM1_MOD - (int)&FTM1_C1V;
  672. DMA_TCD2_DLASTSGA = TIMER_REGISTERS_TO_UPDATE * ((int)&FTM1_C1V - (int)&FTM1_MOD);
  673. // no minor loop linking, single major loop
  674. DMA_TCD2_CITER_ELINKNO = 1;
  675. DMA_TCD2_BITER_ELINKNO = 1;
  676. // link channel 3, enable major channel-to-channel linking, don't clear enable after major loop complete
  677. DMA_TCD2_CSR = (3 << 8) | (1 << 5);
  678. DMAMUX0_CHCFG2 = DMAMUX_SOURCE_LATCH_FALLING_EDGE | DMAMUX_ENABLE;
  679. #define DMA_TCD_MLOFF_MASK (0x3FFFFC00)
  680. // DMA channel #3 - repeatedly load gpio_array into GPIOD_PDOR, stop and int on major loop complete
  681. DMA_TCD3_SADDR = matrixUpdateData[0][0];
  682. DMA_TCD3_SOFF = sizeof(matrixUpdateData[0][0]) / 2;
  683. // SADDR will get updated by ISR, no need to set SLAST
  684. DMA_TCD3_SLAST = 0;
  685. DMA_TCD3_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
  686. // after each minor loop, set source to point back to the beginning of this set of data,
  687. // but advance by 1 byte to get the next significant bits data
  688. DMA_TCD3_NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
  689. (((1 - sizeof(matrixUpdateData[0])) << 10) & DMA_TCD_MLOFF_MASK) |
  690. (MATRIX_WIDTH * DMA_UPDATES_PER_CLOCK);
  691. DMA_TCD3_DADDR = &GPIOD_PDOR;
  692. DMA_TCD3_DOFF = 0;
  693. DMA_TCD3_DLASTSGA = 0;
  694. DMA_TCD3_CITER_ELINKNO = LATCHES_PER_ROW;
  695. DMA_TCD3_BITER_ELINKNO = LATCHES_PER_ROW;
  696. // int after major loop is complete
  697. DMA_TCD3_CSR = DMA_TCD_CSR_INTMAJOR;
  698. // for debugging - enable bandwidth control (space out GPIO updates so they can be seen easier on a low-bandwidth logic analyzer)
  699. //DMA_TCD3_CSR |= (0x02 << 14);
  700. // enable a done interrupt when all DMA operations are complete
  701. NVIC_ENABLE_IRQ(IRQ_DMA_CH3);
  702. // enable additional dma interrupt used as software interrupt
  703. NVIC_SET_PRIORITY(IRQ_DMA_CH1, 0xFF); // 0xFF = lowest priority
  704. NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
  705. // enable channels 0, 1, 2, 3
  706. DMA_ERQ = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
  707. // at the end after everything is set up: enable timer from system clock, with appropriate prescale
  708. FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(LATCH_TIMER_PRESCALE);
  709. */
  710. };
  711. // arrange the relative priority of 2 or more DMA channels
  712. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2);
  713. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3);
  714. void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3, DMAChannel &ch4);
  715. extern "C" {
  716. #endif
  717. extern uint16_t dma_channel_allocated_mask;
  718. #ifdef __cplusplus
  719. }
  720. #endif
  721. #endif // KINETISK
  722. #endif