SPI TFT for STM32F4 boards (#20384)
* fix pinsDebug for F1 boards * add MKS Robin PRO V2 board - development board * tft spi working with F4 boards * pins formating * sanity check for TFT on supported cores in STM32 * Fix tabs/spaces in pins file Co-authored-by: Jason Smith <jason.inet@gmail.com>
This commit is contained in:
		| @@ -51,3 +51,7 @@ | ||||
| #elif ENABLED(SERIAL_STATS_DROPPED_RX) | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on this platform." | ||||
| #endif | ||||
|  | ||||
| #if ANY(TFT_COLOR_UI, TFT_LVGL_UI, TFT_CLASSIC_UI) && NOT_TARGET(STM32F4xx, STM32F1xx) | ||||
|   #error "TFT_COLOR_UI, TFT_LVGL_UI and TFT_CLASSIC_U are currently only supported on STM32F4 and STM32F1 hardware." | ||||
| #endif | ||||
|   | ||||
| @@ -137,32 +137,19 @@ const XrefInfo pin_xref[] PROGMEM = { | ||||
| #endif | ||||
|  | ||||
| uint8_t get_pin_mode(const pin_t Ard_num) { | ||||
|   uint32_t mode_all = 0; | ||||
|   const PinName dp = digitalPinToPinName(Ard_num); | ||||
|   switch (PORT_ALPHA(dp)) { | ||||
|     case 'A' : mode_all = GPIOA->MODER; break; | ||||
|     case 'B' : mode_all = GPIOB->MODER; break; | ||||
|     case 'C' : mode_all = GPIOC->MODER; break; | ||||
|     case 'D' : mode_all = GPIOD->MODER; break; | ||||
|     #ifdef PE_0 | ||||
|       case 'E' : mode_all = GPIOE->MODER; break; | ||||
|     #elif defined(PF_0) | ||||
|       case 'F' : mode_all = GPIOF->MODER; break; | ||||
|     #elif defined(PG_0) | ||||
|       case 'G' : mode_all = GPIOG->MODER; break; | ||||
|     #elif defined(PH_0) | ||||
|       case 'H' : mode_all = GPIOH->MODER; break; | ||||
|     #elif defined(PI_0) | ||||
|       case 'I' : mode_all = GPIOI->MODER; break; | ||||
|     #elif defined(PJ_0) | ||||
|       case 'J' : mode_all = GPIOJ->MODER; break; | ||||
|     #elif defined(PK_0) | ||||
|       case 'K' : mode_all = GPIOK->MODER; break; | ||||
|     #elif defined(PL_0) | ||||
|       case 'L' : mode_all = GPIOL->MODER; break; | ||||
|     #endif | ||||
|   uint32_t ll_pin  = STM_LL_GPIO_PIN(dp); | ||||
|   GPIO_TypeDef *port = get_GPIO_Port(STM_PORT(dp)); | ||||
|   uint32_t mode = LL_GPIO_GetPinMode(port, ll_pin); | ||||
|   switch (mode) | ||||
|   { | ||||
|     case LL_GPIO_MODE_ANALOG: return MODE_PIN_ANALOG; | ||||
|     case LL_GPIO_MODE_INPUT: return MODE_PIN_INPUT; | ||||
|     case LL_GPIO_MODE_OUTPUT: return MODE_PIN_OUTPUT; | ||||
|     case LL_GPIO_MODE_ALTERNATE: return MODE_PIN_ALT; | ||||
|     TERN_(STM32F1xx, case LL_GPIO_MODE_FLOATING:) | ||||
|     default: return 0; | ||||
|   } | ||||
|   return (mode_all >> (2 * uint8_t(PIN_NUM(dp)))) & 0x03; | ||||
| } | ||||
|  | ||||
| bool GET_PINMODE(const pin_t Ard_num) { | ||||
| @@ -217,58 +204,62 @@ bool pwm_status(const pin_t Ard_num) { | ||||
| } | ||||
|  | ||||
| void pwm_details(const pin_t Ard_num) { | ||||
|   if (pwm_status(Ard_num)) { | ||||
|     uint32_t alt_all = 0; | ||||
|     const PinName dp = digitalPinToPinName(Ard_num); | ||||
|     pin_t pin_number = uint8_t(PIN_NUM(dp)); | ||||
|     const bool over_7 = pin_number >= 8; | ||||
|     const uint8_t ind = over_7 ? 1 : 0; | ||||
|     switch (PORT_ALPHA(dp)) {  // get alt function | ||||
|       case 'A' : alt_all = GPIOA->AFR[ind]; break; | ||||
|       case 'B' : alt_all = GPIOB->AFR[ind]; break; | ||||
|       case 'C' : alt_all = GPIOC->AFR[ind]; break; | ||||
|       case 'D' : alt_all = GPIOD->AFR[ind]; break; | ||||
|       #ifdef PE_0 | ||||
|         case 'E' : alt_all = GPIOE->AFR[ind]; break; | ||||
|       #elif defined (PF_0) | ||||
|         case 'F' : alt_all = GPIOF->AFR[ind]; break; | ||||
|       #elif defined (PG_0) | ||||
|         case 'G' : alt_all = GPIOG->AFR[ind]; break; | ||||
|       #elif defined (PH_0) | ||||
|         case 'H' : alt_all = GPIOH->AFR[ind]; break; | ||||
|       #elif defined (PI_0) | ||||
|         case 'I' : alt_all = GPIOI->AFR[ind]; break; | ||||
|       #elif defined (PJ_0) | ||||
|         case 'J' : alt_all = GPIOJ->AFR[ind]; break; | ||||
|       #elif defined (PK_0) | ||||
|         case 'K' : alt_all = GPIOK->AFR[ind]; break; | ||||
|       #elif defined (PL_0) | ||||
|         case 'L' : alt_all = GPIOL->AFR[ind]; break; | ||||
|       #endif | ||||
|     } | ||||
|     if (over_7) pin_number -= 8; | ||||
|   #ifndef STM32F1xx | ||||
|     if (pwm_status(Ard_num)) { | ||||
|       uint32_t alt_all = 0; | ||||
|       const PinName dp = digitalPinToPinName(Ard_num); | ||||
|       pin_t pin_number = uint8_t(PIN_NUM(dp)); | ||||
|       const bool over_7 = pin_number >= 8; | ||||
|       const uint8_t ind = over_7 ? 1 : 0; | ||||
|       switch (PORT_ALPHA(dp)) {  // get alt function | ||||
|         case 'A' : alt_all = GPIOA->AFR[ind]; break; | ||||
|         case 'B' : alt_all = GPIOB->AFR[ind]; break; | ||||
|         case 'C' : alt_all = GPIOC->AFR[ind]; break; | ||||
|         case 'D' : alt_all = GPIOD->AFR[ind]; break; | ||||
|         #ifdef PE_0 | ||||
|           case 'E' : alt_all = GPIOE->AFR[ind]; break; | ||||
|         #elif defined (PF_0) | ||||
|           case 'F' : alt_all = GPIOF->AFR[ind]; break; | ||||
|         #elif defined (PG_0) | ||||
|           case 'G' : alt_all = GPIOG->AFR[ind]; break; | ||||
|         #elif defined (PH_0) | ||||
|           case 'H' : alt_all = GPIOH->AFR[ind]; break; | ||||
|         #elif defined (PI_0) | ||||
|           case 'I' : alt_all = GPIOI->AFR[ind]; break; | ||||
|         #elif defined (PJ_0) | ||||
|           case 'J' : alt_all = GPIOJ->AFR[ind]; break; | ||||
|         #elif defined (PK_0) | ||||
|           case 'K' : alt_all = GPIOK->AFR[ind]; break; | ||||
|         #elif defined (PL_0) | ||||
|           case 'L' : alt_all = GPIOL->AFR[ind]; break; | ||||
|         #endif | ||||
|       } | ||||
|       if (over_7) pin_number -= 8; | ||||
|  | ||||
|     uint8_t alt_func = (alt_all >> (4 * pin_number)) & 0x0F; | ||||
|     SERIAL_ECHOPAIR("Alt Function: ", alt_func); | ||||
|     if (alt_func < 10) SERIAL_CHAR(' '); | ||||
|     SERIAL_ECHOPGM(" - "); | ||||
|     switch (alt_func) { | ||||
|       case  0 : SERIAL_ECHOPGM("system (misc. I/O)"); break; | ||||
|       case  1 : SERIAL_ECHOPGM("TIM1/TIM2 (probably PWM)"); break; | ||||
|       case  2 : SERIAL_ECHOPGM("TIM3..5 (probably PWM)"); break; | ||||
|       case  3 : SERIAL_ECHOPGM("TIM8..11 (probably PWM)"); break; | ||||
|       case  4 : SERIAL_ECHOPGM("I2C1..3"); break; | ||||
|       case  5 : SERIAL_ECHOPGM("SPI1/SPI2"); break; | ||||
|       case  6 : SERIAL_ECHOPGM("SPI3"); break; | ||||
|       case  7 : SERIAL_ECHOPGM("USART1..3"); break; | ||||
|       case  8 : SERIAL_ECHOPGM("USART4..6"); break; | ||||
|       case  9 : SERIAL_ECHOPGM("CAN1/CAN2, TIM12..14  (probably PWM)"); break; | ||||
|       case 10 : SERIAL_ECHOPGM("OTG"); break; | ||||
|       case 11 : SERIAL_ECHOPGM("ETH"); break; | ||||
|       case 12 : SERIAL_ECHOPGM("FSMC, SDIO, OTG"); break; | ||||
|       case 13 : SERIAL_ECHOPGM("DCMI"); break; | ||||
|       case 14 : SERIAL_ECHOPGM("unused (shouldn't see this)"); break; | ||||
|       case 15 : SERIAL_ECHOPGM("EVENTOUT"); break; | ||||
|       uint8_t alt_func = (alt_all >> (4 * pin_number)) & 0x0F; | ||||
|       SERIAL_ECHOPAIR("Alt Function: ", alt_func); | ||||
|       if (alt_func < 10) SERIAL_CHAR(' '); | ||||
|       SERIAL_ECHOPGM(" - "); | ||||
|       switch (alt_func) { | ||||
|         case  0 : SERIAL_ECHOPGM("system (misc. I/O)"); break; | ||||
|         case  1 : SERIAL_ECHOPGM("TIM1/TIM2 (probably PWM)"); break; | ||||
|         case  2 : SERIAL_ECHOPGM("TIM3..5 (probably PWM)"); break; | ||||
|         case  3 : SERIAL_ECHOPGM("TIM8..11 (probably PWM)"); break; | ||||
|         case  4 : SERIAL_ECHOPGM("I2C1..3"); break; | ||||
|         case  5 : SERIAL_ECHOPGM("SPI1/SPI2"); break; | ||||
|         case  6 : SERIAL_ECHOPGM("SPI3"); break; | ||||
|         case  7 : SERIAL_ECHOPGM("USART1..3"); break; | ||||
|         case  8 : SERIAL_ECHOPGM("USART4..6"); break; | ||||
|         case  9 : SERIAL_ECHOPGM("CAN1/CAN2, TIM12..14  (probably PWM)"); break; | ||||
|         case 10 : SERIAL_ECHOPGM("OTG"); break; | ||||
|         case 11 : SERIAL_ECHOPGM("ETH"); break; | ||||
|         case 12 : SERIAL_ECHOPGM("FSMC, SDIO, OTG"); break; | ||||
|         case 13 : SERIAL_ECHOPGM("DCMI"); break; | ||||
|         case 14 : SERIAL_ECHOPGM("unused (shouldn't see this)"); break; | ||||
|         case 15 : SERIAL_ECHOPGM("EVENTOUT"); break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   #else | ||||
|     // TODO: F1 doesn't support changing pins function, so we need to check the function of the PIN and if it's enabled | ||||
|   #endif | ||||
| } // pwm_details | ||||
|   | ||||
| @@ -34,35 +34,25 @@ DMA_HandleTypeDef TFT_SPI::DMAtx; | ||||
| void TFT_SPI::Init() { | ||||
|   SPI_TypeDef *spiInstance; | ||||
|  | ||||
|   #if PIN_EXISTS(TFT_RESET) | ||||
|     OUT_WRITE(TFT_RESET_PIN, HIGH); | ||||
|     HAL_Delay(100); | ||||
|   #endif | ||||
|  | ||||
|   #if PIN_EXISTS(TFT_BACKLIGHT) | ||||
|     OUT_WRITE(TFT_BACKLIGHT_PIN, HIGH); | ||||
|   #endif | ||||
|  | ||||
|   OUT_WRITE(TFT_A0_PIN, HIGH); | ||||
|   OUT_WRITE(TFT_CS_PIN, HIGH); | ||||
|  | ||||
|   if ((spiInstance = (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_SCK_PIN),  PinMap_SPI_SCLK)) == NP) return; | ||||
|   if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MOSI_PIN), PinMap_SPI_MOSI)) return; | ||||
|  | ||||
|   #if PIN_EXISTS(TFT_MISO) && (TFT_MISO_PIN != TFT_MOSI_PIN) | ||||
|     if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO)) return; | ||||
|   #if PIN_EXISTS(TFT_MISO) | ||||
|     if (TFT_MISO_PIN != TFT_MOSI_PIN) | ||||
|       if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO)) return; | ||||
|   #endif | ||||
|  | ||||
|   SPIx.Instance                = spiInstance; | ||||
|   SPIx.State                   = HAL_SPI_STATE_RESET; | ||||
|   SPIx.Init.NSS                = SPI_NSS_SOFT; | ||||
|   SPIx.Init.Mode               = SPI_MODE_MASTER; | ||||
|   SPIx.Init.Direction          = | ||||
|   #if TFT_MISO_PIN == TFT_MOSI_PIN | ||||
|                                  SPI_DIRECTION_1LINE; | ||||
|   #else | ||||
|                                  SPI_DIRECTION_2LINES; | ||||
|   #endif | ||||
|   if (TFT_MISO_PIN == TFT_MOSI_PIN) | ||||
|     SPIx.Init.Direction         = SPI_DIRECTION_1LINE; | ||||
|   else | ||||
|     SPIx.Init.Direction         = SPI_DIRECTION_2LINES; | ||||
|   SPIx.Init.BaudRatePrescaler  = SPI_BAUDRATEPRESCALER_2; | ||||
|   SPIx.Init.CLKPhase           = SPI_PHASE_1EDGE; | ||||
|   SPIx.Init.CLKPolarity        = SPI_POLARITY_LOW; | ||||
| @@ -74,31 +64,50 @@ void TFT_SPI::Init() { | ||||
|  | ||||
|   pinmap_pinout(digitalPinToPinName(TFT_SCK_PIN), PinMap_SPI_SCLK); | ||||
|   pinmap_pinout(digitalPinToPinName(TFT_MOSI_PIN), PinMap_SPI_MOSI); | ||||
|   #if PIN_EXISTS(TFT_MISO) && (TFT_MISO_PIN != TFT_MOSI_PIN) | ||||
|     pinmap_pinout(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO); | ||||
|   #if PIN_EXISTS(TFT_MISO) | ||||
|     if (TFT_MISO_PIN != TFT_MOSI_PIN) | ||||
|       pinmap_pinout(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO); | ||||
|   #endif | ||||
|   pin_PullConfig(get_GPIO_Port(STM_PORT(digitalPinToPinName(TFT_SCK_PIN))), STM_LL_GPIO_PIN(digitalPinToPinName(TFT_SCK_PIN)), GPIO_PULLDOWN); | ||||
|  | ||||
|   #ifdef SPI1_BASE | ||||
|     if (SPIx.Instance == SPI1) { | ||||
|       __HAL_RCC_SPI1_CLK_ENABLE(); | ||||
|       __HAL_RCC_DMA1_CLK_ENABLE(); | ||||
|       #ifdef STM32F1xx | ||||
|         __HAL_RCC_DMA1_CLK_ENABLE(); | ||||
|         DMAtx.Instance = DMA1_Channel3; | ||||
|       #elif defined(STM32F4xx) | ||||
|         __HAL_RCC_DMA2_CLK_ENABLE(); | ||||
|         DMAtx.Instance = DMA2_Stream3; | ||||
|         DMAtx.Init.Channel = DMA_CHANNEL_3; | ||||
|       #endif | ||||
|       SPIx.Init.BaudRatePrescaler  = SPI_BAUDRATEPRESCALER_4; | ||||
|       DMAtx.Instance = DMA1_Channel3; | ||||
|     } | ||||
|   #endif | ||||
|   #ifdef SPI2_BASE | ||||
|     if (SPIx.Instance == SPI2) { | ||||
|       __HAL_RCC_SPI2_CLK_ENABLE(); | ||||
|       __HAL_RCC_DMA1_CLK_ENABLE(); | ||||
|       DMAtx.Instance = DMA1_Channel5; | ||||
|       #ifdef STM32F1xx | ||||
|         __HAL_RCC_DMA1_CLK_ENABLE(); | ||||
|         DMAtx.Instance = DMA1_Channel5; | ||||
|       #elif defined(STM32F4xx) | ||||
|         __HAL_RCC_DMA1_CLK_ENABLE(); | ||||
|         DMAtx.Instance = DMA1_Stream4; | ||||
|         DMAtx.Init.Channel = DMA_CHANNEL_4; | ||||
|       #endif | ||||
|     } | ||||
|   #endif | ||||
|   #ifdef SPI3_BASE | ||||
|     if (SPIx.Instance == SPI3) { | ||||
|       __HAL_RCC_SPI3_CLK_ENABLE(); | ||||
|       __HAL_RCC_DMA2_CLK_ENABLE(); | ||||
|       DMAtx.Instance = DMA2_Channel2; | ||||
|       #ifdef STM32F1xx | ||||
|         __HAL_RCC_DMA2_CLK_ENABLE(); | ||||
|         DMAtx.Instance = DMA2_Channel2; | ||||
|       #elif defined(STM32F4xx) | ||||
|         __HAL_RCC_DMA1_CLK_ENABLE(); | ||||
|         DMAtx.Instance = DMA1_Stream5; | ||||
|         DMAtx.Init.Channel = DMA_CHANNEL_5; | ||||
|       #endif | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
| @@ -110,6 +119,9 @@ void TFT_SPI::Init() { | ||||
|   DMAtx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; | ||||
|   DMAtx.Init.Mode = DMA_NORMAL; | ||||
|   DMAtx.Init.Priority = DMA_PRIORITY_LOW; | ||||
|   #if defined(STM32F4xx) | ||||
|     DMAtx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| void TFT_SPI::DataTransferBegin(uint16_t DataSize) { | ||||
| @@ -142,12 +154,12 @@ uint32_t TFT_SPI::ReadID(uint16_t Reg) { | ||||
|     __HAL_SPI_ENABLE(&SPIx); | ||||
|  | ||||
|     for (i = 0; i < 4; i++) { | ||||
|       #if TFT_MISO_PIN != TFT_MOSI_PIN | ||||
|       if (TFT_MISO_PIN != TFT_MOSI_PIN) { | ||||
|         //if (hspi->Init.Direction == SPI_DIRECTION_2LINES) { | ||||
|           while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} | ||||
|           SPIx.Instance->DR = 0; | ||||
|         //} | ||||
|       #endif | ||||
|       } | ||||
|       while ((SPIx.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {} | ||||
|       Data = (Data << 8) | SPIx.Instance->DR; | ||||
|     } | ||||
| @@ -162,21 +174,34 @@ uint32_t TFT_SPI::ReadID(uint16_t Reg) { | ||||
| } | ||||
|  | ||||
| bool TFT_SPI::isBusy() { | ||||
|   if (DMAtx.Instance->CCR & DMA_CCR_EN) | ||||
|   #if defined(STM32F1xx) | ||||
|     volatile bool dmaEnabled = (DMAtx.Instance->CCR & DMA_CCR_EN) != RESET; | ||||
|   #elif defined(STM32F4xx) | ||||
|     volatile bool dmaEnabled = DMAtx.Instance->CR & DMA_SxCR_EN; | ||||
|   #endif | ||||
|   if (dmaEnabled) { | ||||
|     if (__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TC_FLAG_INDEX(&DMAtx)) != 0 || __HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TE_FLAG_INDEX(&DMAtx)) != 0) | ||||
|       Abort(); | ||||
|   return DMAtx.Instance->CCR & DMA_CCR_EN; | ||||
|   } | ||||
|   else { | ||||
|     Abort(); | ||||
|   } | ||||
|   return dmaEnabled; | ||||
| } | ||||
|  | ||||
| void TFT_SPI::Abort() { | ||||
|   __HAL_DMA_DISABLE(&DMAtx); | ||||
|   // First, abort any running dma | ||||
|   HAL_DMA_Abort(&DMAtx); | ||||
|   // DeInit objects | ||||
|   HAL_DMA_DeInit(&DMAtx); | ||||
|   HAL_SPI_DeInit(&SPIx); | ||||
|   // Deselect CS | ||||
|   DataTransferEnd(); | ||||
| } | ||||
|  | ||||
| void TFT_SPI::Transmit(uint16_t Data) { | ||||
|   #if TFT_MISO_PIN == TFT_MOSI_PIN | ||||
|   if (TFT_MISO_PIN == TFT_MOSI_PIN) | ||||
|     SPI_1LINE_TX(&SPIx); | ||||
|   #endif | ||||
|  | ||||
|   __HAL_SPI_ENABLE(&SPIx); | ||||
|  | ||||
| @@ -185,26 +210,23 @@ void TFT_SPI::Transmit(uint16_t Data) { | ||||
|   while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} | ||||
|   while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {} | ||||
|  | ||||
|   #if TFT_MISO_PIN != TFT_MOSI_PIN | ||||
|   if (TFT_MISO_PIN != TFT_MOSI_PIN) | ||||
|     __HAL_SPI_CLEAR_OVRFLAG(&SPIx);   /* Clear overrun flag in 2 Lines communication mode because received is not read */ | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| void TFT_SPI::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) { | ||||
|   // Wait last dma finish, to start another | ||||
|   while(isBusy()) { } | ||||
|  | ||||
|   DMAtx.Init.MemInc = MemoryIncrease; | ||||
|   HAL_DMA_Init(&DMAtx); | ||||
|  | ||||
|   if (TFT_MISO_PIN == TFT_MOSI_PIN) | ||||
|     SPI_1LINE_TX(&SPIx); | ||||
|  | ||||
|   DataTransferBegin(); | ||||
|  | ||||
|   #if TFT_MISO_PIN == TFT_MOSI_PIN | ||||
|     SPI_1LINE_TX(&SPIx); | ||||
|   #endif | ||||
|  | ||||
|   DMAtx.DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << DMAtx.ChannelIndex); | ||||
|   DMAtx.Instance->CNDTR = Count; | ||||
|   DMAtx.Instance->CPAR = (uint32_t)&(SPIx.Instance->DR); | ||||
|   DMAtx.Instance->CMAR = (uint32_t)Data; | ||||
|   __HAL_DMA_ENABLE(&DMAtx); | ||||
|   HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(SPIx.Instance->DR), Count); | ||||
|   __HAL_SPI_ENABLE(&SPIx); | ||||
|  | ||||
|   SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN);   /* Enable Tx DMA Request */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user