Try   HackMD
tags: Embedded

STM32F4 開發紀錄

使用 ALIENTEK正點原子 探索者 STM32F407 開發板

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

CPU: Cortex-M4 (ARMv7-M 架構)
FLASH: 1Mbytes
RAM: 128 KB

開發環境

市面上常見用來開發 Cortex-M 的 IDE 整理在下表

IDE 名稱 Pros Cons
Keil µVision 適合新手,不需要太多環境的設置就能開始寫程式,算是最簡單能夠上手的 IDE 此為授權軟體,免費版限制程式大小最大為 32 KB,超過則需要付費。
另外不知道為何,我用 STM32CubeMX 產生的程式碼跟我設定的有誤,在 STM32CubeIDE 就沒這問題
STM32CubeIDE 專門用來寫 STM32 的 IDE,各種功能都整合得很好,因為是基於 Eclipse,所以有用過 Eclipse 大概會覺得很親切 沒什麼缺點,硬要說的話就是設定比 µVision 多一點,autocomplete 不像 µVision 能夠自動觸發
GNU MCU Eclipse 完全開源的 IDE 多年前用過覺得設定相當複雜,不但要了解檔案樹,還要懂 openocd、linker 等,看了一個月可能都還不會用

使用 Keil µVision 如果超過 32KB,則會出現以下錯誤

error: L6050U: The code size of this image (66612 bytes) exceeds the maximum allowed for this version of the linker.

STM32CubeIDE 環境架設

先開啟一個 Project,並試著燒錄程式到板子上。我的 Debugger 叫 fireDAP,所以使用 openocd,在 Run Configuration ➔ 調試器 (debugger) ➔ 調試探頭 中選擇 ST-Link (OpenOCD),並且在底下的 Configuration Script 中選擇 User Defined,接著我們要修改 openocd 的腳本

在 xxx.cfg 的檔案中修改 source 跟 transport 這兩行,根據你使用的 debugger 會有所不同

source [find interface/cmsis-dap.cfg]
transport select "swd"

按一下 Run 的箭頭應該就可以燒錄了

設定 Semihosting

Semihosting 可以讓 Target 跟主機用 debugger 來做溝通,所以就不用另外用一個 UART 做 debug。具體來說,可以在板子上呼叫 printf,IDE 的 Console 就可以看到結果,或者是把除錯資料輸出到一個檔案中,方便之後做 debug。

以下這篇說明得很清楚,照做就可以了,exclude 那一欄要注意路徑,在我的 Project 裡要是設定 Src/syscalls.c
https://shawnhymel.com/1840/how-to-use-semihosting-with-stm32/

到目前為止基本的設定都做好了

在不需要 Debug 的時候應該要註解掉所有 printf 及相關輸出的操作,否則之後在燒錄的時候就不能正常運作,程式會卡在 printf 裡。

試試看點亮板子上的 LED 確認一切都能正常運作

int main(void)
{
  ...
  while (1) {
    HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9);
    HAL_Delay(250);
  }
}

GPIO port output speed register (GPIOx_OSPEEDR)

這個暫存器可以控制 slew rate,也就是電壓從 low 到 high 或是從 high 到 low 所需要的時間,STM32F407 提供四種速度

  • 00: Low speed
  • 01: Medium speed
  • 10: High speed
  • 11: Very high speed

參考 http://fastbitlab.com/gpio-output-speed-register-applicability/

GPIO Features

From STM32F4-Technical-Training

  • Up to 140 multifunction bi-directional I/O ports available on 176 pin package
  • Almost standard I/Os are 5V tolerant
  • GPIO connected to AHB bus: max toggling frequency = fAHB/2 = 84 MHz

GPIO 外部中斷

參考
http://wiki.csie.ncku.edu.tw/embedded/GPIO#stm32f4xx-gpio特性

先來試試最簡單的外部中斷,剛好板子上有按鈕,就直接拿來用,硬體電路圖如下

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

使用 KEY2,這個按鈕最後連接到 PE2 腳位,沒有消除彈跳的硬體,下面三個按鈕都連到接地,所以之後要在 STM32 晶片裡設定 PULL-UP。

開啟外部中斷有幾個步驟

  1. 在 STM32CudeMX 中腳位設定為 GPIO_EXTI,因為我們是用 PULL-UP,所以使用 External Interrupt Mode with Falling edge trigger detection

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  1. (重要!) 到 NVIC 裡把對應的 EXIT line interrupt 的 Enabled 框框打勾,以 PE2 腳位來說就是對應 line2

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  1. 實作自己的 HAL_GPIO_EXTI_Callback

Prototype 為

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

Q. STM32F4 明明不同的 EXTI line 都有對應的外部中斷函數,為什麼不同 line 的 callback 都是使用 HAL_GPIO_EXTI_Callback?

void EXTI0_IRQHandler();
void EXTI1_IRQHandler();
void EXTI2_IRQHandler();
void EXTI3_IRQHandler();
void EXTI4_IRQHandler();
void EXTI9_5_IRQHandler();
void EXTI15_10_IRQHandler();

A. HAL 函數庫對以上 IRQ 做封裝,他們最後都會呼叫使用者定義的 HAL_GPIO_EXTI_Callback,使用者必須檢查 GPIO_Pin 來得知這是哪一條 line 的外部中斷

簡單跑一下試試看,順便檢查是否有按鍵彈跳會重複觸發外部中斷

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  static int time = 0;
  printf("activate %d\n", time++);
}

輸出結果,果不其然按鍵有彈跳,當我按一下按鍵會同時印出好幾行

target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000894 msp: 0x20020000, semihosting
configuring PLL
Info : High speed (adapter speed 8000) may be limited by adapter firmware.
Info : Padding image section 0 at 0x08000188 with 8 bytes
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000894 msp: 0x20020000, semihosting
activate 0
activate 1
activate 2
activate 3
activate 4
activate 5
activate 6
activate 7
activate 8
activate 9
activate 10
activate 11

PWM

參考
http://wiki.csie.ncku.edu.tw/embedded/PWM

試試看 STM32F4 的 PWM 功能,嘗試讓板子上的 LED 變成呼吸燈。

PWM 功能有兩個重要的暫存器,ARR (autoreload register) 和 CRR,Tim14 只有一個通道所以只有一個 CCR 暫存器叫 CCR1

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

使用以下設定

Counter Settings:

  • Prescaler: 這會設定內部時鐘的除頻數值,TIM14 對應 APB1 Timer,最大頻率是 84Mhz,這裡我設定 APB1 Timer clock 為 16 Mhz,所以 TIM14 是以 16Mhz/16 = 1Mhz 的頻率在計數
  • Counter Mode: Up mode,計時器往上計數
  • Counter Period: 也就是 ARR 數值,設定計時器的溢位數值,這裡才決定了 PWM 的頻率 1Mhz / 1000 = 1khz

PWM Generation Channel 1:

  • PWM Mode 1 - Channel 1 is active as long as TIMx_CNT < TIMx_CCR1 else inactive,也就是計時器數值小於 CCR1 時是 active
  • Pulse: CCR1 數值,控制 PWM 的 duty cycle
  • CH Polarity: Low,代表 active 時輸出低電平

因為開發版上的 LED 是低電平亮,所以 Polarity 設定 Low,讓 CCR1 數值 (duty cycle) 越大時 LED 越亮,反之越暗。

以下盡量只列出使用者程式碼的部分

TIM_HandleTypeDef htim14;

int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t dir = 1;
  uint16_t led0pwmval = 0;
  ...
  MX_TIM14_Init();
  HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1);
  led0pwmval = htim14.Instance->CCR1;
  while (1) {
    HAL_Delay(2);
    if (dir) led0pwmval++;
    else led0pwmval--;
    if (led0pwmval >= htim14.Instance->ARR) dir = 0;
    if (led0pwmval == 0) dir = 1;
    htim14.Instance->CCR1 = led0pwmval;
  }

Ethernet

Active PHY Address

This is an identifier that tells you which of the physical PHYs were used to interface to the network. The numbers range from 0 - 31 and change, depending on whether or not you specified a specific PHY or if you let the driver select the default (which varies from card to card).

PHY 外部晶片在初始化時,需要指定 PHY Address (非實體記憶體),數值介於 0 到 31 之間,因為一個 MAC 介面最多可以控制 32 個 PHY,這張板子的 PHY 設定在 0,用以下的程式碼設定。

heth.Init.PhyAddress = LAN8720_PHY_ADDRESS;

與 EtherCAT 驅動器串聯

我使用開源軟體叫做 SOEM,此專案提供輕量級的 EtherCAT 函式庫,由於官方並沒有支援 stm32 架構,因此必須動手修改程式碼,由於 SOEM 軟體架構做了很明確的分層,所要修改的程式碼並不多,要修改檔案的目錄如下

  • osal: 大概是 OS application layer,此目錄要利用目標硬體實作 gettimeofdayosal_usleep,使用 timer 就能很簡單的做出 osal_usleep,而 gettimeofday 大概需要 RTC,但我是寫死一個起始數值,從開機後累加時間
  • oshw: 這部分必須實作 Ethernet 的初始化,傳送以及接收訊框,可使用 HAL_ETH_* 系列函式實作。

搞定上面兩個目錄 SOEM 就能移植到 stm32 的板子上了,如果遇到問題有很大的機率是 Ethernet 初始化沒設定好。

我遇到的問題是使用 auto negotiation 無法直接連接,板子和驅動器之間必須接上 switch 才能正常互通,把 auto negotiation 關掉再調整一些 timing 就成功連上驅動器了。

stm32f4 移植參考的專案: https://os.mbed.com/users/EasyCAT/code/SOEM//file/543d6784d4cc/oshw/nicdrv.cpp/

nicdrv.c 會是需要修改最多的檔案,如果是使用 STM32cubeIDE 設定 ETH,ETH 初始化對應的程式碼會自動產生,所以就不用另外寫了。 (但還是要注意 auto negotiation,STM32cubeIDE 似乎沒有處理 disable 的情況)

HAL_ETH_Init 修改的部分
HAL_StatusTypeDef HAL_ETH_Init(ETH_HandleTypeDef *heth)
{
  uint32_t tmpreg1 = 0U, phyreg = 0U;
  uint32_t hclk = 60000000U;
  uint32_t tickstart = 0U;
  uint32_t err = ETH_SUCCESS;
  
  /* Check the ETH peripheral state */
  if(heth == NULL)
  {
    return HAL_ERROR;
  }
  
  /* Check parameters */
  assert_param(IS_ETH_AUTONEGOTIATION(heth->Init.AutoNegotiation));
  assert_param(IS_ETH_RX_MODE(heth->Init.RxMode));
  assert_param(IS_ETH_CHECKSUM_MODE(heth->Init.ChecksumMode));
  assert_param(IS_ETH_MEDIA_INTERFACE(heth->Init.MediaInterface));  
  
  if(heth->State == HAL_ETH_STATE_RESET)
  {
    /* Allocate lock resource and initialize it */
    heth->Lock = HAL_UNLOCKED;
#if (USE_HAL_ETH_REGISTER_CALLBACKS == 1)
    ETH_InitCallbacksToDefault(heth);

    if(heth->MspInitCallback == NULL)
    {
      /* Init the low level hardware : GPIO, CLOCK, NVIC. */
      heth->MspInitCallback = HAL_ETH_MspInit;
    }
    heth->MspInitCallback(heth);

#else
    /* Init the low level hardware : GPIO, CLOCK, NVIC. */
    HAL_ETH_MspInit(heth);
#endif /* USE_HAL_ETH_REGISTER_CALLBACKS */
  }
  
  /* Enable SYSCFG Clock */
  __HAL_RCC_SYSCFG_CLK_ENABLE();
  
  /* Select MII or RMII Mode*/
  SYSCFG->PMC &= ~(SYSCFG_PMC_MII_RMII_SEL);
  SYSCFG->PMC |= (uint32_t)heth->Init.MediaInterface;
  
  /* Ethernet Software reset */
  /* Set the SWR bit: resets all MAC subsystem internal registers and logic */
  /* After reset all the registers holds their respective reset values */
  (heth->Instance)->DMABMR |= ETH_DMABMR_SR;
  
  /* Get tick */
  tickstart = HAL_GetTick();
  
  /* Wait for software reset */
  while (((heth->Instance)->DMABMR & ETH_DMABMR_SR) != (uint32_t)RESET)
  {
    /* Check for the Timeout */
    if((HAL_GetTick() - tickstart ) > ETH_TIMEOUT_SWRESET)
    {     
      heth->State= HAL_ETH_STATE_TIMEOUT;
  
      /* Process Unlocked */
      __HAL_UNLOCK(heth);
    
      /* Note: The SWR is not performed if the ETH_RX_CLK or the ETH_TX_CLK are  
         not available, please check your external PHY or the IO configuration */
      return HAL_TIMEOUT;
    }
  }
  
  /*-------------------------------- MAC Initialization ----------------------*/
  /* Get the ETHERNET MACMIIAR value */
  tmpreg1 = (heth->Instance)->MACMIIAR;
  /* Clear CSR Clock Range CR[2:0] bits */
  tmpreg1 &= ETH_MACMIIAR_CR_MASK;
  
  /* Get hclk frequency value */
  hclk = HAL_RCC_GetHCLKFreq();
  
  /* Set CR bits depending on hclk value */
  if((hclk >= 20000000U)&&(hclk < 35000000U))
  {
    /* CSR Clock Range between 20-35 MHz */
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_Div16;
  }
  else if((hclk >= 35000000U)&&(hclk < 60000000U))
  {
    /* CSR Clock Range between 35-60 MHz */ 
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_Div26;
  }  
  else if((hclk >= 60000000U)&&(hclk < 100000000U))
  {
    /* CSR Clock Range between 60-100 MHz */ 
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_Div42;
  }  
  else if((hclk >= 100000000U)&&(hclk < 150000000U))
  {
    /* CSR Clock Range between 100-150 MHz */ 
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_Div62;
  }
  else /* ((hclk >= 150000000)&&(hclk <= 183000000)) */
  {
    /* CSR Clock Range between 150-183 MHz */ 
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_Div102;    
  }
  
  /* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */
  (heth->Instance)->MACMIIAR = (uint32_t)tmpreg1;
  
  /*-------------------- PHY initialization and configuration ----------------*/
  /* Put the PHY in reset mode */
  if((HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_RESET)) != HAL_OK)
  {
    /* In case of write timeout */
    err = ETH_ERROR;
    
    /* Config MAC and DMA */
    ETH_MACDMAConfig(heth, err);
    
    /* Set the ETH peripheral state to READY */
    heth->State = HAL_ETH_STATE_READY;
    
    /* Return HAL_ERROR */
    return HAL_ERROR;
  }
  
  /* Delay to assure PHY reset */
  HAL_Delay(PHY_RESET_DELAY);
  
  if((heth->Init).AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE)
  {
    /* Get tick */
    tickstart = HAL_GetTick();
    
    /* We wait for linked status */
    do
    {
      HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);
      
      /* Check for the Timeout */
      if((HAL_GetTick() - tickstart ) > ETH_TIMEOUT_LINKED_STATE)
      {
        /* In case of write timeout */
        err = ETH_ERROR;
      
        /* Config MAC and DMA */
        ETH_MACDMAConfig(heth, err);
        
        heth->State= HAL_ETH_STATE_READY;
  
        /* Process Unlocked */
        __HAL_UNLOCK(heth);
    
        return HAL_TIMEOUT;
      }
    } while (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS));

    
    /* Enable Auto-Negotiation */
    if((HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_AUTONEGOTIATION)) != HAL_OK)
    {
      /* In case of write timeout */
      err = ETH_ERROR;
      
      /* Config MAC and DMA */
      ETH_MACDMAConfig(heth, err);
      
      /* Set the ETH peripheral state to READY */
      heth->State = HAL_ETH_STATE_READY;
      
      /* Return HAL_ERROR */
      return HAL_ERROR;   
    }
    
    /* Get tick */
    tickstart = HAL_GetTick();
    
    /* Wait until the auto-negotiation will be completed */
    do
    {
      HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);
      
      /* Check for the Timeout */
      if((HAL_GetTick() - tickstart ) > ETH_TIMEOUT_AUTONEGO_COMPLETED)
      {
        /* In case of write timeout */
        err = ETH_ERROR;
      
        /* Config MAC and DMA */
        ETH_MACDMAConfig(heth, err);
        
        heth->State= HAL_ETH_STATE_READY;
  
        /* Process Unlocked */
        __HAL_UNLOCK(heth);
    
        return HAL_TIMEOUT;
      }
      
    } while (((phyreg & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE));
    
    /* Read the result of the auto-negotiation */
    if((HAL_ETH_ReadPHYRegister(heth, PHY_SR, &phyreg)) != HAL_OK)
    {
      /* In case of write timeout */
      err = ETH_ERROR;
      
      /* Config MAC and DMA */
      ETH_MACDMAConfig(heth, err);
      
      /* Set the ETH peripheral state to READY */
      heth->State = HAL_ETH_STATE_READY;
      
      /* Return HAL_ERROR */
      return HAL_ERROR;   
    }
    
    /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
    if((phyreg & PHY_DUPLEX_STATUS) != (uint32_t)RESET)
    {
      /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */
      (heth->Init).DuplexMode = ETH_MODE_FULLDUPLEX;  
    }
    else
    {
      /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */
      (heth->Init).DuplexMode = ETH_MODE_HALFDUPLEX;           
    }
    /* Configure the MAC with the speed fixed by the auto-negotiation process */
    if((phyreg & PHY_SPEED_STATUS) == PHY_SPEED_STATUS)
    {  
      /* Set Ethernet speed to 10M following the auto-negotiation */
      (heth->Init).Speed = ETH_SPEED_10M; 
    }
    else
    {   
      /* Set Ethernet speed to 100M following the auto-negotiation */ 
      (heth->Init).Speed = ETH_SPEED_100M;
    }
  }
  else /* AutoNegotiation Disable */
  {
    /* Check parameters */
    assert_param(IS_ETH_SPEED(heth->Init.Speed));
    assert_param(IS_ETH_DUPLEX_MODE(heth->Init.DuplexMode));

+   /* Check link is up */
+   uint32_t reg;
+   do {
+       HAL_Delay(20);
+   	HAL_ETH_ReadPHYRegister(heth, 1, &reg);
+   } while (!(reg & (1 << 2)));

    /* Set MAC Speed and Duplex Mode */
    if(HAL_ETH_WritePHYRegister(heth, PHY_BCR, ((uint16_t)((heth->Init).DuplexMode >> 3U) |
                                                (uint16_t)((heth->Init).Speed >> 1U))) != HAL_OK)
    {
      /* In case of write timeout */
      err = ETH_ERROR;
      
      /* Config MAC and DMA */
      ETH_MACDMAConfig(heth, err);
      
      /* Set the ETH peripheral state to READY */
      heth->State = HAL_ETH_STATE_READY;
      
      /* Return HAL_ERROR */
      return HAL_ERROR;
    }  
    
    /* Delay to assure PHY configuration */
    HAL_Delay(PHY_CONFIG_DELAY);
  }
  
  /* Config MAC and DMA */
  ETH_MACDMAConfig(heth, err);
  
  /* Set ETH HAL State to Ready */
  heth->State= HAL_ETH_STATE_READY;
  
  /* Return function status */
  return HAL_OK;
}

EtherCAT

PDO 分為兩種,在規格書 (CiA402) 中稱作 Transmit-PDOs (TPDOs) and Receive-PDOs (RPDOs),但台達手冊稱作 TxPDO 和 RxPDO。

TxPDO 和 RxPDO 是以從站的角度來看,自從站發出的訊息是 TxPDO,接收的訊息叫 RxPDO

Index Start from
TxPDO 0x1A00
RxPDO 0x1600

TxPDO0 index: 0x1A00
TxPDO1 index: 0x1A01
以此推類

Mapping Layout

The mappings are held in the sub-indexes and are encoded as 32-bit unsigned integers
參考: https://stackoverflow.com/questions/27132341/how-to-setup-pdo-mapping

      16-bit        8-bit    8-bit
+--------+--------+--------+--------+
|      index      |subindex|  size  |
+--------+--------+--------+--------+
                                `-> size in bits of the parameter

還未修改 PDO Mapping 之前驅動器的設定如下所示,注意 SM2 和 SM3 兩列輸出,他們分別對應 RxPDO 和 TxPDO。

$ sudo ./test/linux/slaveinfo/slaveinfo enp4s0 -map
SOEM (Simple Open EtherCAT Master)
Slaveinfo
Starting slaveinfo
ec_init on enp4s0 succeeded.
1 slaves found and configured.
Calculated workcounter 3

Slave:1
 Name:? M:000001dd I:00006010
 Output size: 48bits
 Input size: 48bits
 State: 4
 Delay: 0[ns]
 Has DC: 1
 DCParentport:0
 Activeports:1.0.0.0
 Configured address: 1001
 Man: 000001dd ID: 00006010 Rev: 00030000
 SM0 A:1000 L: 128 F:00010036 Type:1
 SM1 A:10c0 L: 128 F:00010032 Type:2
 SM2 A:1180 L:   6 F:00010024 Type:3
 SM3 A:1480 L:   6 F:00010000 Type:4
 FMMU0 Ls:00000000 Ll:   6 Lsb:0 Leb:7 Ps:1180 Psb:0 Ty:02 Act:01
 FMMU1 Ls:00000006 Ll:   6 Lsb:0 Leb:7 Ps:1480 Psb:0 Ty:01 Act:01
 FMMUfunc 0:1 1:2 2:3 3:0
 MBX length wr: 128 rd: 128 MBX protocols : 04
 CoE details: 0f FoE details: 00 EoE details: 00 SoE details: 00
 Ebus current: 0[mA]
 only LRD/LWR:0
PDO mapping according to CoE :
  SM2 outputs
     addr b   index: sub bitl data_type    name
  [0x0000.0] 0x6040:0x00 0x10 UNSIGNED16   
  [0x0002.0] 0x607A:0x00 0x20 INTEGER32    
  SM3 inputs
     addr b   index: sub bitl data_type    name
  [0x0006.0] 0x6041:0x00 0x10 UNSIGNED16   
  [0x0008.0] 0x6064:0x00 0x20 INTEGER32    
End slaveinfo, close socket
End program

修改 PDO Mapping 之後預期可見 SM2 和 SM3 的輸出改變

$ sudo ./test/linux/slaveinfo/slaveinfo enp4s0 -map
SOEM (Simple Open EtherCAT Master)
Slaveinfo
Starting slaveinfo
ec_init on enp4s0 succeeded.
1 slaves found and configured.
Calculated workcounter 3

Slave:1
 Name:? M:000001dd I:00006010
 Output size: 112bits
 Input size: 176bits
 State: 4
 Delay: 0[ns]
 Has DC: 1
 DCParentport:0
 Activeports:1.0.0.0
 Configured address: 1001
 Man: 000001dd ID: 00006010 Rev: 00030000
 SM0 A:1000 L: 128 F:00010036 Type:1
 SM1 A:10c0 L: 128 F:00010032 Type:2
 SM2 A:1180 L:  14 F:00010024 Type:3
 SM3 A:1480 L:  22 F:00010000 Type:4
 FMMU0 Ls:00000000 Ll:  14 Lsb:0 Leb:7 Ps:1180 Psb:0 Ty:02 Act:01
 FMMU1 Ls:0000000e Ll:  22 Lsb:0 Leb:7 Ps:1480 Psb:0 Ty:01 Act:01
 FMMUfunc 0:1 1:2 2:3 3:0
 MBX length wr: 128 rd: 128 MBX protocols : 04
 CoE details: 0f FoE details: 00 EoE details: 00 SoE details: 00
 Ebus current: 0[mA]
 only LRD/LWR:0
PDO mapping according to CoE :
  SM2 outputs
     addr b   index: sub bitl data_type    name
  [0x0000.0] 0x6040:0x00 0x10 UNSIGNED16   
  [0x0002.0] 0x607A:0x00 0x20 INTEGER32    
  [0x0006.0] 0x60FF:0x00 0x20 INTEGER32    
  [0x000A.0] 0x6071:0x00 0x10 INTEGER16    
  [0x000C.0] 0x60B8:0x00 0x10 UNSIGNED16   
  SM3 inputs
     addr b   index: sub bitl data_type    name
  [0x000E.0] 0x6041:0x00 0x10 UNSIGNED16   
  [0x0010.0] 0x6064:0x00 0x20 INTEGER32    
  [0x0014.0] 0x606C:0x00 0x20 INTEGER32    
  [0x0018.0] 0x6077:0x00 0x10 INTEGER16    
  [0x001A.0] 0x60B9:0x00 0x10 UNSIGNED16   
  [0x001C.0] 0x60BA:0x00 0x20 INTEGER32    
  [0x0020.0] 0x60FD:0x00 0x20 UNSIGNED32   
End slaveinfo, close socket
End program

TFT-LCD

我用的是以下這片觸控螢幕

LCD 驅動晶片 電容觸控驅動晶片 解析度
SSD1963 FT5206 800*480

實際使用

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
注意如果初始化後出現畫面閃爍,或是印出雜訊,極有可能是 FSMC 的 timing 沒有設定好,很大可能是因為設得太小導致的!

可嘗試使用以下設定:

  Timing.AddressSetupTime = 1 * 4;
  Timing.AddressHoldTime = 15 * 2;
  Timing.DataSetupTime = 2 * 4;
  Timing.BusTurnAroundDuration = 15 * 2;
  Timing.CLKDivision = 16 * 2;
  Timing.DataLatency = 17 * 2;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;

參考:
https://arm-stm.blogspot.com/2016/12/ssd1963-init-collection.html
https://www.youtube.com/watch?v=dQoy4wQpcTI&ab_channel=EEbyKarl
https://support.touchgfx.com/zh-TW/docs/development/touchgfx-hal-development/touchgfx-generator