My Marlin configs for Fabrikator Mini and CTC i3 Pro B
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.

spi_impl.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. #ifdef TARGET_LPC1768
  2. #include "../spi_api.h"
  3. #include <lpc17xx_ssp.h>
  4. #include <lpc17xx_pinsel.h>
  5. #include <lpc17xx_gpio.h>
  6. #include "lpc17xx_clkpwr.h"
  7. extern "C" void SSP0_IRQHandler(void);
  8. extern "C" void SSP1_IRQHandler(void);
  9. namespace HAL {
  10. namespace SPI {
  11. enum class SignalPolarity : uint8_t {
  12. ACTIVE_LOW = 0,
  13. ACTIVE_HIGH
  14. };
  15. /* Hardware channels :
  16. * 0: clk(0_7), mosi(0_9), miso(0_8), SSP1
  17. * 1: clk(0_15), mosi(0_28), miso(0_17), SSP0
  18. * Logical channels:
  19. * 0: hwchannel: 1, ssel(0_6)
  20. * 1: hwchannel: 0, ssel(0_16)
  21. * 2: hwchannel: 0, ssel(1_23)
  22. */
  23. /*
  24. * Defines the Hardware setup for an SPI channel
  25. * The pins and (if applicable) the Hardware Peripheral
  26. *
  27. */
  28. struct LogicalChannel; // who doesn't like circular dependencies
  29. struct HardwareChannel {
  30. LPC_SSP_TypeDef *peripheral;
  31. IRQn_Type IRQn;
  32. uint8_t clk_port;
  33. uint8_t clk_pin;
  34. uint8_t mosi_port;
  35. uint8_t mosi_pin;
  36. uint8_t miso_port;
  37. uint8_t miso_pin;
  38. SSP_DATA_SETUP_Type xfer_config;
  39. volatile FlagStatus xfer_complete;
  40. bool initialised;
  41. volatile bool in_use;
  42. LogicalChannel* active_channel;
  43. } hardware_channels[2] = {
  44. {LPC_SSP0, SSP0_IRQn, 0, 15, 0, 18, 0, 17, { nullptr, 0, nullptr, 0, 0, SSP_STAT_DONE }, RESET, false, false, nullptr},
  45. {LPC_SSP1, SSP1_IRQn, 0, 7, 0, 9, 0, 8 , { nullptr, 0, nullptr, 0, 0, SSP_STAT_DONE }, RESET, false, false, nullptr}
  46. };
  47. /*
  48. * Define all available logical SPI ports
  49. */
  50. struct LogicalChannel {
  51. HardwareChannel& hw_channel;
  52. uint8_t ssel_port;
  53. uint8_t ssel_pin;
  54. SignalPolarity ssel_polarity;
  55. bool ssel_override;
  56. SSP_CFG_Type config;
  57. uint32_t CR0;
  58. uint32_t CPSR;
  59. } logical_channels[3] = {
  60. { hardware_channels[1], 0, 6, SignalPolarity::ACTIVE_LOW, false, { SSP_DATABIT_8, SSP_CPHA_FIRST, SSP_CPOL_HI, SSP_MASTER_MODE, SSP_FRAME_SPI, 1000000 }, 0, 0 },
  61. { hardware_channels[0], 0, 16, SignalPolarity::ACTIVE_HIGH, true, { SSP_DATABIT_8, SSP_CPHA_FIRST, SSP_CPOL_HI, SSP_MASTER_MODE, SSP_FRAME_SPI, 1000000 }, 0, 0 },
  62. { hardware_channels[0], 1, 23, SignalPolarity::ACTIVE_LOW, true, { SSP_DATABIT_8, SSP_CPHA_FIRST, SSP_CPOL_HI, SSP_MASTER_MODE, SSP_FRAME_SPI, 1000000 }, 0, 0 }
  63. };
  64. //Internal functions
  65. extern "C" void ssp_irq_handler(uint8_t hw_channel);
  66. LogicalChannel* get_logical_channel(uint8_t channel);
  67. bool set_ssel(LogicalChannel* logical_channel);
  68. void clear_ssel(LogicalChannel* logical_channel);
  69. void restore_frequency(LogicalChannel* logical_channel);
  70. LogicalChannel* get_logical_channel(uint8_t channel) {
  71. if(channel > sizeof(logical_channels) - 1) {
  72. return nullptr;
  73. }
  74. return &logical_channels[channel];
  75. }
  76. bool set_ssel(LogicalChannel* logical_channel) {
  77. if(logical_channel->hw_channel.in_use == true) {
  78. return false;
  79. }
  80. if(logical_channel->ssel_polarity == SignalPolarity::ACTIVE_HIGH) {
  81. GPIO_SetValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
  82. } else {
  83. GPIO_ClearValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
  84. }
  85. logical_channel->hw_channel.in_use = true;
  86. return true;
  87. }
  88. void clear_ssel(LogicalChannel* logical_channel) {
  89. if(logical_channel->ssel_polarity == SignalPolarity::ACTIVE_HIGH) {
  90. GPIO_ClearValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
  91. } else {
  92. GPIO_SetValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
  93. }
  94. logical_channel->hw_channel.in_use = false;
  95. }
  96. void restore_frequency(LogicalChannel* logical_channel) {
  97. logical_channel->hw_channel.peripheral->CR0 = logical_channel->CR0;
  98. logical_channel->hw_channel.peripheral->CPSR = logical_channel->CPSR;
  99. }
  100. /*
  101. * SPI API Implementation
  102. */
  103. bool initialise(uint8_t channel) {
  104. LogicalChannel* logical_channel = get_logical_channel(channel);
  105. if(logical_channel == nullptr) return false;
  106. HardwareChannel& hw_channel = logical_channel->hw_channel;
  107. PINSEL_CFG_Type pin_cfg;
  108. pin_cfg.OpenDrain = PINSEL_PINMODE_NORMAL;
  109. pin_cfg.Pinmode = PINSEL_PINMODE_PULLUP;
  110. if(hw_channel.initialised == false) {
  111. pin_cfg.Funcnum = 2; //ssp (spi) function
  112. pin_cfg.Portnum = hw_channel.clk_port;
  113. pin_cfg.Pinnum = hw_channel.clk_pin;
  114. PINSEL_ConfigPin(&pin_cfg); //clk
  115. pin_cfg.Portnum = hw_channel.miso_port;
  116. pin_cfg.Pinnum = hw_channel.miso_pin;
  117. PINSEL_ConfigPin(&pin_cfg); //miso
  118. pin_cfg.Portnum = hw_channel.mosi_port;
  119. pin_cfg.Pinnum = hw_channel.mosi_pin;
  120. PINSEL_ConfigPin(&pin_cfg); //mosi
  121. SSP_Init(hw_channel.peripheral, &logical_channel->config);
  122. logical_channel->CR0 = logical_channel->hw_channel.peripheral->CR0; // preserve for restore
  123. logical_channel->CPSR = logical_channel->hw_channel.peripheral->CPSR; // preserve for restore
  124. SSP_Cmd(hw_channel.peripheral, ENABLE);
  125. hw_channel.initialised = true;
  126. hw_channel.active_channel = logical_channel;
  127. //NVIC_SetPriority(hw_channel.IRQn, NVIC_EncodePriority(0, 3, 0)); //Very Low priority
  128. //NVIC_EnableIRQ(hw_channel.IRQn);
  129. }
  130. pin_cfg.Portnum = logical_channel->ssel_port;
  131. pin_cfg.Pinnum = logical_channel->ssel_pin;
  132. pin_cfg.Pinmode = logical_channel->ssel_polarity == SignalPolarity::ACTIVE_LOW ? PINSEL_PINMODE_PULLUP : PINSEL_PINMODE_PULLDOWN;
  133. pin_cfg.Funcnum = 0; //gpio function
  134. PINSEL_ConfigPin(&pin_cfg); //ssel
  135. GPIO_SetDir(logical_channel->ssel_port, (1 << logical_channel->ssel_pin), 1);
  136. GPIO_SetValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
  137. return true;
  138. }
  139. bool enable_cs(uint8_t channel) {
  140. LogicalChannel* logical_channel = get_logical_channel(channel);
  141. if(logical_channel == nullptr) return false;
  142. return set_ssel(logical_channel);
  143. }
  144. void disable_cs(uint8_t channel) {
  145. LogicalChannel* logical_channel = get_logical_channel(channel);
  146. if(logical_channel == nullptr) return;
  147. if(logical_channel->hw_channel.in_use && !logical_channel->ssel_override) return; //automatic SSel wasn't overridden
  148. clear_ssel(logical_channel);
  149. }
  150. void set_frequency(uint8_t channel, uint32_t frequency) {
  151. LogicalChannel* logical_channel = get_logical_channel(channel);
  152. if(logical_channel == nullptr) return;
  153. SSP_Cmd(logical_channel->hw_channel.peripheral, DISABLE);
  154. uint32_t prescale, cr0_div, cmp_clk, ssp_clk;
  155. if (logical_channel->hw_channel.peripheral == LPC_SSP0){
  156. ssp_clk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_SSP0);
  157. } else if (logical_channel->hw_channel.peripheral == LPC_SSP1) {
  158. ssp_clk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_SSP1);
  159. } else {
  160. return;
  161. }
  162. //find the closest clock divider / prescaler
  163. cr0_div = 0;
  164. cmp_clk = 0xFFFFFFFF;
  165. prescale = 2;
  166. while (cmp_clk > frequency) {
  167. cmp_clk = ssp_clk / ((cr0_div + 1) * prescale);
  168. if (cmp_clk > frequency) {
  169. cr0_div++;
  170. if (cr0_div > 0xFF) {
  171. cr0_div = 0;
  172. prescale += 2;
  173. }
  174. }
  175. }
  176. logical_channel->hw_channel.peripheral->CR0 &= (~SSP_CR0_SCR(0xFF)) & SSP_CR0_BITMASK;
  177. logical_channel->hw_channel.peripheral->CR0 |= (SSP_CR0_SCR(cr0_div)) & SSP_CR0_BITMASK;
  178. logical_channel->CR0 = logical_channel->hw_channel.peripheral->CR0; // preserve for restore
  179. logical_channel->hw_channel.peripheral->CPSR = prescale & SSP_CPSR_BITMASK;
  180. logical_channel->CPSR = logical_channel->hw_channel.peripheral->CPSR; // preserve for restore
  181. logical_channel->config.ClockRate = ssp_clk / ((cr0_div + 1) * prescale);
  182. SSP_Cmd(logical_channel->hw_channel.peripheral, ENABLE);
  183. }
  184. void read(uint8_t channel, uint8_t *buffer, uint32_t length) {
  185. transfer(channel, nullptr, buffer, length);
  186. }
  187. uint8_t read(uint8_t channel) {
  188. uint8_t buffer;
  189. transfer(channel, nullptr, &buffer, 1);
  190. return buffer;
  191. }
  192. void write(uint8_t channel, const uint8_t *buffer, uint32_t length) {
  193. transfer(channel, buffer, nullptr, length);
  194. }
  195. void write(uint8_t channel, uint8_t value) {
  196. transfer(channel, &value, nullptr, 1);
  197. }
  198. void transfer(uint8_t channel, const uint8_t *buffer_write, uint8_t *buffer_read, uint32_t length) {
  199. LogicalChannel* logical_channel = get_logical_channel(channel);
  200. if(logical_channel == nullptr) return;
  201. if((logical_channel->hw_channel.in_use && !logical_channel->ssel_override) || !logical_channel->hw_channel.initialised) return;
  202. if(!logical_channel->ssel_override) {
  203. if(!set_ssel(logical_channel)) return;
  204. }
  205. if(logical_channel != logical_channel->hw_channel.active_channel) {
  206. restore_frequency(logical_channel);
  207. logical_channel->hw_channel.active_channel = logical_channel;
  208. }
  209. logical_channel->hw_channel.xfer_config.tx_data = (void *)buffer_write;
  210. logical_channel->hw_channel.xfer_config.rx_data = (void *)buffer_read;
  211. logical_channel->hw_channel.xfer_config.length = length;
  212. (void)SSP_ReadWrite(logical_channel->hw_channel.peripheral, &logical_channel->hw_channel.xfer_config, SSP_TRANSFER_POLLING); //SSP_TRANSFER_INTERRUPT
  213. if(!logical_channel->ssel_override) {
  214. clear_ssel(logical_channel->hw_channel.active_channel);
  215. }
  216. }
  217. uint8_t transfer(uint8_t channel, uint8_t value) {
  218. uint8_t buffer;
  219. transfer(channel, &value, &buffer, 1);
  220. return buffer;
  221. }
  222. /*
  223. * Interrupt Handlers
  224. */
  225. extern "C" void ssp_irq_handler(uint8_t hw_channel) {
  226. SSP_DATA_SETUP_Type *xf_setup;
  227. uint32_t tmp;
  228. uint8_t dataword;
  229. // Disable all SSP interrupts
  230. SSP_IntConfig(hardware_channels[hw_channel].peripheral, SSP_INTCFG_ROR | SSP_INTCFG_RT | SSP_INTCFG_RX | SSP_INTCFG_TX, DISABLE);
  231. dataword = (SSP_GetDataSize(hardware_channels[hw_channel].peripheral) > 8) ? 1 : 0;
  232. xf_setup = &hardware_channels[hw_channel].xfer_config;
  233. // save status
  234. tmp = SSP_GetRawIntStatusReg(hardware_channels[hw_channel].peripheral);
  235. xf_setup->status = tmp;
  236. // Check overrun error
  237. if (tmp & SSP_RIS_ROR) {
  238. // Clear interrupt
  239. SSP_ClearIntPending(hardware_channels[hw_channel].peripheral, SSP_INTCLR_ROR);
  240. // update status
  241. xf_setup->status |= SSP_STAT_ERROR;
  242. // Set Complete Flag
  243. hardware_channels[hw_channel].xfer_complete = SET;
  244. if(!hardware_channels[hw_channel].active_channel->ssel_override) clear_ssel(hardware_channels[hw_channel].active_channel);
  245. return;
  246. }
  247. if ((xf_setup->tx_cnt != xf_setup->length) || (xf_setup->rx_cnt != xf_setup->length)) {
  248. /* check if RX FIFO contains data */
  249. while ((SSP_GetStatus(hardware_channels[hw_channel].peripheral, SSP_STAT_RXFIFO_NOTEMPTY)) && (xf_setup->rx_cnt != xf_setup->length)) {
  250. // Read data from SSP data
  251. tmp = SSP_ReceiveData(hardware_channels[hw_channel].peripheral);
  252. // Store data to destination
  253. if (xf_setup->rx_data != nullptr) {
  254. if (dataword == 0) {
  255. *(uint8_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint8_t) tmp;
  256. } else {
  257. *(uint16_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint16_t) tmp;
  258. }
  259. }
  260. // Increase counter
  261. if (dataword == 0) {
  262. xf_setup->rx_cnt++;
  263. } else {
  264. xf_setup->rx_cnt += 2;
  265. }
  266. }
  267. while ((SSP_GetStatus(hardware_channels[hw_channel].peripheral, SSP_STAT_TXFIFO_NOTFULL)) && (xf_setup->tx_cnt != xf_setup->length)) {
  268. // Write data to buffer
  269. if (xf_setup->tx_data == nullptr) {
  270. if (dataword == 0) {
  271. SSP_SendData(hardware_channels[hw_channel].peripheral, 0xFF);
  272. xf_setup->tx_cnt++;
  273. } else {
  274. SSP_SendData(hardware_channels[hw_channel].peripheral, 0xFFFF);
  275. xf_setup->tx_cnt += 2;
  276. }
  277. } else {
  278. if (dataword == 0) {
  279. SSP_SendData(hardware_channels[hw_channel].peripheral, (*(uint8_t *) ((uint32_t) xf_setup->tx_data + xf_setup->tx_cnt)));
  280. xf_setup->tx_cnt++;
  281. } else {
  282. SSP_SendData(hardware_channels[hw_channel].peripheral, (*(uint16_t *) ((uint32_t) xf_setup->tx_data + xf_setup->tx_cnt)));
  283. xf_setup->tx_cnt += 2;
  284. }
  285. }
  286. // Check overrun error
  287. if (SSP_GetRawIntStatus(hardware_channels[hw_channel].peripheral, SSP_INTSTAT_RAW_ROR)) {
  288. // update status
  289. xf_setup->status |= SSP_STAT_ERROR;
  290. // Set Complete Flag
  291. hardware_channels[hw_channel].xfer_complete = SET;
  292. if(!hardware_channels[hw_channel].active_channel->ssel_override) clear_ssel(hardware_channels[hw_channel].active_channel);
  293. return;
  294. }
  295. // Check for any data available in RX FIFO
  296. while ((SSP_GetStatus(hardware_channels[hw_channel].peripheral, SSP_STAT_RXFIFO_NOTEMPTY)) && (xf_setup->rx_cnt != xf_setup->length)) {
  297. // Read data from SSP data
  298. tmp = SSP_ReceiveData(hardware_channels[hw_channel].peripheral);
  299. // Store data to destination
  300. if (xf_setup->rx_data != nullptr) {
  301. if (dataword == 0) {
  302. *(uint8_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint8_t) tmp;
  303. } else {
  304. *(uint16_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint16_t) tmp;
  305. }
  306. }
  307. // Increase counter
  308. if (dataword == 0) {
  309. xf_setup->rx_cnt++;
  310. } else {
  311. xf_setup->rx_cnt += 2;
  312. }
  313. }
  314. }
  315. }
  316. // If there more data to sent or receive
  317. if ((xf_setup->rx_cnt != xf_setup->length) || (xf_setup->tx_cnt != xf_setup->length)) {
  318. // Enable all interrupt
  319. SSP_IntConfig(hardware_channels[hw_channel].peripheral, SSP_INTCFG_ROR | SSP_INTCFG_RT | SSP_INTCFG_RX | SSP_INTCFG_TX, ENABLE);
  320. } else {
  321. // Save status
  322. xf_setup->status = SSP_STAT_DONE;
  323. // Set Complete Flag
  324. hardware_channels[hw_channel].xfer_complete = SET;
  325. if(!hardware_channels[hw_channel].active_channel->ssel_override) clear_ssel(hardware_channels[hw_channel].active_channel);
  326. }
  327. }
  328. }
  329. }
  330. extern "C" void SSP0_IRQHandler(void) {
  331. HAL::SPI::ssp_irq_handler(0);
  332. }
  333. extern "C" void SSP1_IRQHandler(void) {
  334. HAL::SPI::ssp_irq_handler(1);
  335. }
  336. #endif