顯示包含「STM32」標籤的文章。顯示所有文章
顯示包含「STM32」標籤的文章。顯示所有文章

2022年12月23日星期五

[STM32] FreeRTOS 手動移植至 CubeIDE 過程記錄

FreeRTOS

FreeRTOS 是一種即時作業系統,非常適用於微控制器應用。當然 STM32 也是在其中最適合於 FreeRTOS 操作的微控制器,這次來介紹 CubeIDE 環境中如何手動加入 FreeRTOS 系統。這次使用的開發板是 NUCLEO-L476RG,IDE 是 CubeIDE。

下載 FreeRTOS

在 FreerRTOS (https://www.freertos.org/) 直接下載,目前是 FreeRTOSv202112.00 這個版本

STM32,FreeRTOS

建構 FreeRTOS

下載後,解開壓縮檔。有幾個資料夾及檔案 (FreeRTOS,FreeRTOS-Plus,tools .....etc),我們只要 " FreeRTOS " 這個資料夾及內容。我採取的是刪除法,先建立一個空的資料夾,然後將 FreeRTOS 複製到剛建立的資料夾內。

複製完成後,直接在這個臨時資料夾內操作。

FreeRTOS 資料夾內有 Demo , License , Source , Test 等等內容。保留 License , Source,其他都刪除。

直接到 Source\portable 內 ,保留 GCC , 及 MemMang 這兩個資料夾,其他刪除。

L476RG 是個 M4 及含 FPU 運算的微控制器,所以

在 Source\portable\GCC 內, 只保留 ARM_CM4F 資料夾。

回到 MemMang 內
保留 heap_4.c , 其餘刪除

到這裡我們需要的檔案已經完成了。

CubeIDE 開啟新專案

我們設定 2 個 GPIO-OUT , 分別是 LD1 & LD2

STM32,FreeRTOS

在 NVIC 中斷設定, 優先權設為 NVIC_PriorityGroup_4

STM32,FreeRTOS

接著產生 BSP code。

調整

產生 BSP code 後,開始做相對應的設定。
複製 FreeRTOS code。

在 "建構 FreeRTOS" 所以建立的資料夾, 直接用拖拉的方式。複製到 CubeIDE 左側的 Project Exploper。

STM32,FreeRTOS

開啟 Project / Properties 對話框

找 C/C++ General / Paths and Symbols , 分別在 Includes , Source Location 這兩個標籤 (Tab) 設定

Includes :

FreeRTOS/Source/include
FreeRTOS/Source/portable/GCC/ARM_CM4F

STM32,FreeRTOS

Source Location :

加入 FreeRTOS/Source

STM32,FreeRTOS

修改 stm32l4xx_it.c


void SysTick_Handler(void)
void PendSV_Handler(void)
void SVC_Handler(void)

3個 function 用 /**/ 注釋掉

建立 FreeRTOSConfig.h


在 FreeRTOS 中找到
FreeRTOS/Demo/CORTEX_M4F_STM32F407ZG-SK/ FreeRTOSConfig.h
複製到 專案資料夾的 Core/Inc 內


內容做小修改


#ifdef __ICCARM__
    #include 
    extern uint32_t SystemCoreClock;
#endif

改成符合 CubeIDE


#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
    #include 
    extern uint32_t SystemCoreClock;
#endif

以下 2 個參數改為 0


#define configUSE_IDLE_HOOK                0
#define configUSE_TICK_HOOK                0

範例: 測試點亮 LED

到這裡, 開始寫 code
首先在 main() 前複製 這 2 個空 function


void vApplicationMallocFailedHook( void )
{
    /* vApplicationMallocFailedHook() will only be called if
    configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  It is a hook
    function that will get called if a call to pvPortMalloc() fails.
    pvPortMalloc() is called internally by the kernel whenever a task, queue,
    timer or semaphore is created.  It is also called by various parts of the
    demo application.  If heap_1.c or heap_2.c are used, then the size of the
    heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
    FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
    to query the size of free heap space that remains (although it does not
    provide information on how the remaining heap might be fragmented). */
    taskDISABLE_INTERRUPTS();


    for( ;; );
}




void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
    /* This function will get called if a task overflows its stack.   If the
    parameters are corrupt then inspect pxCurrentTCB to find which was the
    offending task. */


    ( void ) pxTask;
    ( void ) pcTaskName;


    for( ;; );
}

我們有 2 個 LED , 所以分別用 2 個 Thread 控制


static void Blink1_Task(void *pvParameters)
{
TickType_t xlastFlashTime;
xlastFlashTime = xTaskGetTickCount();


while(1){
     vTaskDelayUntil(&xlastFlashTime,500);


     HAL_GPIO_TogglePin(GPIOA, LD1_Pin);


}


}


static void Blink2_Task(void *pvParameters)
{
TickType_t xlastFlashTime;
xlastFlashTime = xTaskGetTickCount();


while(1){
     vTaskDelayUntil(&xlastFlashTime,500);


     HAL_GPIO_TogglePin(GPIOA, LD2_Pin);


}


}

在 main()


  xTaskCreate(Blink1_Task,"Blink1",128,NULL,1,NULL);
  xTaskCreate(Blink2_Task,"Blink2",128,NULL,2,NULL);
  vTaskStartScheduler();

執行效果是這 2 個 LED 看起來會像是同時間點亮。


範例 Source code 放在 Github,
https://github.com/cold63/STM32_Code/tree/master/Freertos_M4F

 

2022年8月9日星期二

[STM32] FreeRTOS 手動移植至 CubeIDE 過程記錄

FreeRTOS 是一種即時作業系統,非常適用於微控制器應用。當然 STM32 也是在其中最適合於 FreeRTOS 操作的微控制器,這次來介紹 CubeIDE 環境中如何手動加入 FreeRTOS 系統。這次使用的開發板是 NUCLEO-L476RG,IDE 是 CubeIDE。

下載 FreeRTOS

在 FreerRTOS (https://www.freertos.org/) 直接下載,目前是 FreeRTOSv202112.00 這個版本

STM32,FreeRTOS

建構 FreeRTOS

下載後,解開壓縮檔。有幾個資料夾及檔案 (FreeRTOS,FreeRTOS-Plus,tools .....etc),我們只要 " FreeRTOS " 這個資料夾及內容。我採取的是刪除法,先建立一個空的資料夾,然後將 FreeRTOS 複製到剛建立的資料夾內。

複製完成後,直接在這個臨時資料夾內操作。

FreeRTOS 資料夾內有 Demo , License , Source , Test 等等內容。保留 License , Source,其他都刪除。

直接到 Source\portable 內 ,保留 GCC , 及 MemMang 這兩個資料夾,其他刪除。

L476RG 是個 M4 及含 FPU 運算的微控制器,所以

在 Source\portable\GCC 內, 只保留 ARM_CM4F 資料夾。

回到 MemMang 內
保留 heap_4.c , 其餘刪除

到這裡我們需要的檔案已經完成了。

CubeIDE 開啟新專案

我們設定 2 個 GPIO-OUT , 分別是 LD1 & LD2

STM32,FreeRTOS

在 NVIC 中斷設定, 優先權設為 NVIC_PriorityGroup_4

STM32,FreeRTOS

接著產生 BSP code。

調整

產生 BSP code 後,開始做相對應的設定。
複製 FreeRTOS code。

在 "建構 FreeRTOS" 所以建立的資料夾, 直接用拖拉的方式。複製到 CubeIDE 左側的 Project Exploper。

STM32,FreeRTOS

開啟 Project / Properties 對話框

找 C/C++ General / Paths and Symbols , 分別在 Includes , Source Location 這兩個標籤 (Tab) 設定

Includes :

FreeRTOS/Source/include
FreeRTOS/Source/portable/GCC/ARM_CM4F

STM32,FreeRTOS

Source Location :

加入 FreeRTOS/Source

STM32,FreeRTOS

修改 stm32l4xx_it.c


void SysTick_Handler(void)
void PendSV_Handler(void)
void SVC_Handler(void)

3個 function 用 /**/ 注釋掉

建立 FreeRTOSConfig.h


在 FreeRTOS 中找到
FreeRTOS/Demo/CORTEX_M4F_STM32F407ZG-SK/ FreeRTOSConfig.h
複製到 專案資料夾的 Core/Inc 內


內容做小修改


#ifdef __ICCARM__
    #include 
    extern uint32_t SystemCoreClock;
#endif

改成符合 CubeIDE


#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
    #include 
    extern uint32_t SystemCoreClock;
#endif

以下 2 個參數改為 0


#define configUSE_IDLE_HOOK                0
#define configUSE_TICK_HOOK                0

範例: 測試點亮 LED

到這裡, 開始寫 code
首先在 main() 前複製 這 2 個空 function


void vApplicationMallocFailedHook( void )
{
    /* vApplicationMallocFailedHook() will only be called if
    configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  It is a hook
    function that will get called if a call to pvPortMalloc() fails.
    pvPortMalloc() is called internally by the kernel whenever a task, queue,
    timer or semaphore is created.  It is also called by various parts of the
    demo application.  If heap_1.c or heap_2.c are used, then the size of the
    heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
    FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
    to query the size of free heap space that remains (although it does not
    provide information on how the remaining heap might be fragmented). */
    taskDISABLE_INTERRUPTS();


    for( ;; );
}




void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
    /* This function will get called if a task overflows its stack.   If the
    parameters are corrupt then inspect pxCurrentTCB to find which was the
    offending task. */


    ( void ) pxTask;
    ( void ) pcTaskName;


    for( ;; );
}

我們有 2 個 LED , 所以分別用 2 個 Thread 控制


static void Blink1_Task(void *pvParameters)
{
TickType_t xlastFlashTime;
xlastFlashTime = xTaskGetTickCount();


while(1){
     vTaskDelayUntil(&xlastFlashTime,500);


     HAL_GPIO_TogglePin(GPIOA, LD1_Pin);


}


}


static void Blink2_Task(void *pvParameters)
{
TickType_t xlastFlashTime;
xlastFlashTime = xTaskGetTickCount();


while(1){
     vTaskDelayUntil(&xlastFlashTime,500);


     HAL_GPIO_TogglePin(GPIOA, LD2_Pin);


}


}

在 main()


  xTaskCreate(Blink1_Task,"Blink1",128,NULL,1,NULL);
  xTaskCreate(Blink2_Task,"Blink2",128,NULL,2,NULL);
  vTaskStartScheduler();

執行效果是這 2 個 LED 看起來會像是同時間點亮。


範例 Source code 放在 Github,
https://github.com/cold63/STM32_Code/tree/master/Freertos_M4F

 

2022年6月8日星期三

[STM32] 通過 I2C 控制 LCD1602

PCF8574 是擴展 I/O 的晶片並透過 I2C 協議通訊。主控端可以通過 PCF8574 暫存器進行 I/O 端的讀取狀態或是做為輸出端口。

datasheet 連結 (https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf)

STM32,1602

PCF8574 Address 定義

STM32,1602

若要調整 Address , 則短接接地即可。


LCD1602

4 bit 初始流程

STM32,LCD1602

指令集

STM32,LCD1602

連接表格

PINDEFINEPCF8574 PIN
1VSS 
2VCC 
3VEE 
4RSP0
5R/WP1
6ENP2
7DB0 
8DB1 
9DB2 
10DB3 
11DB4P4
12DB5P5
13DB6P6
14DB7P7
15LED+123
16LED-123

程式碼


void lcd_send_cmd(char cmd)
{
    char data_h,data_l;
    uint8_t frame_data[4];
    data_h = (cmd&0xf0);
    data_l = ((cmd <<4)&0xf0);
    frame_data[0] = data_h | 0x0C;    //en=1, rs=0
    frame_data[1] = data_h | 0x08;    //en=0, rs=0
    frame_data[2] = data_l | 0x0C;    //en=1, rs=0
    frame_data[3] = data_l | 0x08;    //en=0, rs=0

    HAL_I2C_Master_Transmit(&hi2c1,LCD_ADDRESS,(uint8_t *)frame_data,4,0x100);

    HAL_Delay(1);
}


void lcd_send_data(char data)
{
    char data_h,data_l;
    uint8_t frame_data[4];
    data_h = (data&0xf0);
    data_l = ((data <<4)&0xf0);
    frame_data[0] = data_h | 0x0D;    //en=1, rs=1
    frame_data[1] = data_h | 0x09;    //en=0, rs=1
    frame_data[2] = data_l | 0x0D;    //en=1, rs=1
    frame_data[3] = data_l | 0x09;    //en=0, rs=1

    HAL_I2C_Master_Transmit(&hi2c1,LCD_ADDRESS,(uint8_t *)frame_data,4,0x100);
    HAL_Delay(1);
}


void lcd_clear()
{
    lcd_send_cmd(0x01);
    HAL_Delay(1);
}


void lcd_Init()
{
    HAL_Delay(50);
    lcd_send_cmd(0x30);
    HAL_Delay(5);
    lcd_send_cmd(0x30);
    HAL_Delay(1);
    lcd_send_cmd(0x30);
    HAL_Delay(10);
    lcd_send_cmd(0x20);
    HAL_Delay(10);


    lcd_send_cmd(0x28);        //function set
    HAL_Delay(1);
    lcd_send_cmd(0x08);        //Display on/off
    HAL_Delay(1);
    lcd_send_cmd(0x01);        //clear display
    HAL_Delay(1);
    lcd_send_cmd(0x06);        //Enter mode
    HAL_Delay(1);
    lcd_send_cmd(0x0C);        //Display on/off
    HAL_Delay(1);


}


void lcd_send_string (char *str)
{
    while(*str)
    {
        lcd_send_data(*str++);
    }
    HAL_Delay(1);
}


void lcd_put_cur(uint8_t row,uint8_t col)
{
    lcd_send_cmd(0x80 | (col + (0x40 * row)));
}

主控程式段



lcd_Init();


  /* USER CODE END 2 */


  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
      lcd_put_cur(0,2);
      lcd_send_string("Hello World!");
      lcd_put_cur(1,1);
      lcd_send_string("www.makdev.net");
      HAL_Delay(1000);
      lcd_clear();
      HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }

顯示輸出

STM32,LCD1602

原始碼連結

https://github.com/cold63/STM32_Code/tree/master/L476G-LCD

 

2022年5月3日星期二

[STM32] 透過 ADC 數值控制 PWM 及 伺服馬達 Servo

最近在使用 Arduino AmebaIot 控制 SG90 Servo, 心想也有一段時間沒在寫 STM32 的 Code了,想要練練手感所以發起這個小實驗。想要目的結果是透過 VR可變電阻改變 ADC 值,確認比例之後。間接改變 Servo 的 0度 ~ 180度。 總結來說,會用到的功能環節是 ADC 讀值 , PWM 空占比控制。

 

MCU 設定

這次使用自製的開發板,MCU 為 K051。除了基本設定外,個別加入 ADC IN 及 Time PWM Out 設定
ADC -> ADC_IN8
TIMER -> TIM3_CH2

STM32 PWM

Time 設定 ,使用 Tim3 的 Channel2 。

STM32 PWM

ADC 輸入

使用 Polling 方式輸入 , 請參考 ADC 轉換器 - 使用 Polling 多通道輪詢模式 (連結)
這裡是透過 可變電阻 VR 影響 ADC IN 的輸入值,進而計算出當時的比例值
提示 : STM32 預設解析度為 12 bit = 4096

可變電阻 VR 電路

STM32 PWM ADC

例如:
ADC_in = 1024
比例值 = 1024 /4096 = 25%

Servo 控制

SG-90 可控制角度為 0度 ~ 180 度之間。控制方式需要輸入50Hz 的 PWM 訊號,由訊號寬度來判別控制角度。寬度範圍是 0.5ms ~ 2.5ms 間。

0.5ms 寬度 -> -90 度
1.5ms 寬度 -> 0 度
2.5ms 寬度 -> 90 度

頻率 50Hz = 1/50 = 0.02s = 20ms,所以SG-90 的控制範圍是

0.5ms = 0.5 / 20 = 2.5%
1.5ms = 1.5 / 20 = 7.5%
2.5ms = 2.5 / 20 = 12.5%

STM32 Timer / PWM 設定

MCU CLOCK = 48000000

目標 50 Hz

所以,48000000 / 50 = 960000 ,如果 Period 設定為 1000 ,
則 Prescaler 可以設定為 960000 / 1000 = 960

因此要調整 PWM 空佔比的範圍是 0 ~ 1000
假設需要 1.5ms 的 PWM 寬度 = (1.5ms / 20 ) x 1000 = 75

綜合以上, 導出以下公式


PWMValue = ((2.5*val)/20)*TimePeriod;

val 為 ADC 計算出的比例。

在 STM32 的 HAL 庫, 設定空佔比。可用


__HAL_TIM_SET_COMPARE()

主要控制程式碼


  while (1)
  {
    /* USER CODE END WHILE */
      HAL_ADC_Start(&hadc);


      if(HAL_ADC_PollForConversion(&hadc,100) == HAL_OK)
      {
          ADCValue = HAL_ADC_GetValue(&hadc);
      }
      val = (float)ADCValue/pow(2,12);
      PWMValue = ((2.5*val)/20)*TimePeriod;
      printf("Count[%ld],Value= [%d] ,VR[%d]\%%,PWM value[%d]\r\n",x,(uint16_t)ADCValue ,(uint8_t)((val) * 100),PWMValue);


      if(PWMValue < 25)
      {
          __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,25);
      }
      else
      {
          __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,PWMValue);
      }




      HAL_Delay(250);
      x++;
    /* USER CODE BEGIN 3 */
  }

實際上在示波器測試結果

2.5ms

STM32,PWM,ADC

0.5ms

STM32,PWM,ADC

實體測試電路

原始碼連結

https://github.com/cold63/STM32_Code/tree/master/K051KRunPWM

2022年4月18日星期一

[STM32] STM32 核心測試板 / Cortex-M0 & RS485 版

測試板用途

平常在執行各 Device 測試時,一開始都會先使用 ST 開發板先行測試。而目前工作領域時常會用到 I2C , UART , SPI , RS485 等通訊協定。為了能儘早順利接線直接上線測試,因此設計了專門做測試用的 ST Contex-M0 測試板, 及另外的用途是用來做測試製具主板使用。

關於 STM32F051K8T6

測試板核心使用的型號是 STM32F051K8T6 / Cortex-M0 , 運作頻率為 48Mhz。板子使用內建 Internal 8 MHz RC 震盪器 內存 64 kByte / SRAM 8 kByte, 並搭配 RS485 轉換晶片。

STM051K8T6 規格書

原廠連結 ( https://www.st.com/resource/en/datasheet/stm32f051c4.pdf )

測試板佈局

  • 電源 5V 輸入且具有電源反接功能。
  • 3.3V 電壓輸出。
  • GPIO x 11
  • ADC x 3
  • DAC x 1
  • TIME x 9 
  • UART x 1
  • I2C x 1  (預接 2.7K Pull 電阻)
  • SPI x 1
  • USR Key x 1
  • 內建 RS485 (MAX485ESA+) 輸出

在一般做 Device 測試應該已足夠用了。

USR Key

RS485 輸出

腳位定義

電路圖

STM32Cube 設定

STM32Cube 測試板參考設計

GitHub 連結 ( https://github.com/cold63/STM32_Code/tree/master/K051K8TMCUConfig )

購買開發板

7-11 賣貨便 (Only Taiwan)

 

2022年2月20日星期日

[RS485] USB 轉 RS485 轉換器設計

關於 RS485

在產品研發工作中,時常遇到 RS485 的功能要求。在研發初期確實也需要類似於 Monitor 的監看工具,以方便了解傳輸過程是否有錯誤及了解客戶端的裝置是否有正確反饋資料。這時候就會需要 USB RS485 這類轉換工具,當然這也不限定於開發環境中,因為 RS485 通訊協定在工控領域中,是非常常見的。

RS485 是利用差動訊號來辨別 0 與 1 ,重點特性是能抗雜訊及通訊實體線路長距離傳輸。RS485 是 multi-drop network (多點網路),相同接線可以同時連接不同的 RS485 裝置。在於有多點網路特性及 RS485 為半雙工通訊模式,所以實際傳輸上通常會使用 Master-slave 架構,較為常見的軟體協議是 MODBUS。不過更多的情境是各家系統自訂通訊協議,因此需要有工具才能好好的做除錯的工作,更多延伸資訊可參考 RS485 wiki(中文)RS485 wiki(English)

收發器 ( Transceivers )

在單晶片場域中如果要用 RS485 協議,就必須要使用 RS485 收發器 ( Transceivers ),單晶片是 TTL 準位並不適合直接對接,必須要用 收發器 這點就有點像 CAN bus。(恩,這是題外話),我目前手上的是 Maxim 的 MAX485ESA+ 。 Maxim 已被併入 ANALOG DEVICES 旗下了,更顯得我手上的 MAX485ESA+ 是個珍品阿。剛看 Mouser 上的售價單價是 NT$128 元,我是不是應該要繼續珍藏 ?

RS485
(圖片來自 Maxim spec.)

上圖是 RS485 收發器的內部等效圖,分別是 2 各三態閘分別負責 TX 與 RX 。RO 負責接收,由 /RE 做接收控制。 DI 負責傳送,由 DE 做訊息發控制。A與 B 就是 RS485輸出端了。 與裝置連接通常是使用雙絞線方式連接。而連接器沒有特別指定。這類的的收發器有很多廠家都有,Pin out 基本上都相同,也可以交互使用。 這裡就不推薦了,之後有機會再介紹。



USB 轉 UART

來到 USB 這個環節了,在設計上是使用 FTDI FT230XQ 這個型號。是個 USB 轉 UART TTL 準位的晶片,在很多的場域都會看到 FTDI 這個廠牌。

UART
(圖片來自 FTDI spec.)

這是 FT230XQ 接腳圖,就是個 USB 轉 UART 常用輸出腳位。 有 TXD , RXD , RTS , CTS,但重點是它有 4 個 可自定義的腳位,就是這篇 RS485 的主題要使用的部分。剛提到 RS485 收發器 有 /RE 及 DE 腳做收發控制,在這自定義就發揮這個功能。

CBUS0 可以定義為 TXDEN 為專屬於 RS485 使用,當 USB Host 端發出傳送資料需求時,TXDEN 會轉態為 High,資料就會透過 TXD 跟著傳至 RS485 收發器的 DI 腳 完成資料發出的任務。
CBUS3 可以定義為 PWREN。 這是 USB 處於 suspend mode 模式時會被轉態成 Low。

利用這個特性連接 RS485 收發器的 /RE 腳,使UART待機時隨時是接收模式狀態。所以會有個缺點,若是在做 TXD 傳輸時也會同時收到自己傳輸的資料。不過還好,這問題並不大可以透過軟體來解決。

定義功能可透過 FTDI 的軟體工具設定

UART
(圖片來自 FTDI FT_Prog 軟體)

電路圖

RS485

線路是按照 FTDI 標準線路設計,除了,RS485 連接器。也保留 UART 輸出,可以兩用的形式不浪費,但 CBUS3 最好設回 SLEEP 模式。USB 連接器使用 USB B type,我想工具類的環節最好用 B Type 會比較穩固點。

製作完成

RS485

USB Host 應用

這類的軟體應用很多,但因為工作關係。通常我會自行開發 Host 端的部分,因為比較符合客戶需求及實際使用情況,或是大家有甚麼可推薦的軟體? 可以在底下留言哦!

2021年11月5日星期五

[STM32] ADC 轉換器 - 使用 DMA 多通道模式

 

STM32 ADC


緣起


針對前一篇的 Polling 多通道輪詢模式 方式做個簡單的比較。


基本設定


這次使用 NUCLEO-F401RE 開發版測試,執行 STM32CubeIDE 後直接進入 ADC1 設定項目,選擇其中 3 個 ADC Input 。

IN0    ->    PA0
IN1    ->    PA1
IN4    ->    PA4
STM32 ADC


DMA Settings 的標籤設定


先點取 Add ,選擇 ADC1

DMA Request Settings  的 Mode 選擇 Circular  ,連續DMA請求

STM32 ADC



Parameter Settings 標籤


Resoltion 維持 12 Bits 解析能力,也就是 0 ~ 4095 範圍。

Scan Conversion Mode 設定 Enabled 

Continuous Conversion Mode 設定 Enabled

DMA  Continuous Requests   設定  Enabled

Number Of Conversion 選擇 3 ,表示有 3 組ADC Iinput

Rank 要各自選擇 Channel

Sampling Time    這裡建議初步先設定最大值 480 Cycles

其他設定項目維持 Default 值。
STM32 ADC


儲存後 ,由 STM32CubeIDE 產生程式碼。


程式碼


打開 main.c

首先建立 2 個 Value


uint8_t x;
uint16_t ADCArray[3];

在 While 主迴圈之前啟動 ADC DMA , 最後的參數是指請求 DMA 時的資料筆數。目前設 3 個通道及陣列就直接設定 3 。這裡可以按照 3 的倍數擴大取得資料筆數,不過這裡先簡單使用。

if(HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADCArray,3) != HAL_OK)
{
       printf("ADC initialization error!\r\n");
}

while 主迴圈

  while (1)
  {
    /* USER CODE END WHILE */
         HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
         printf("idx[%d] value[%d]\r\n",x,ADCArray[x] );
         x++;
         if(x >=3)
         {
               x = 0;
               printf("\r\n");
         }
         HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
3 組 ADC Input 的資料會自動儲存到記憶體也就是陣列裡。

好了,這樣就完成了。

簡易的測試方法是將剛剛設定的 ADC 腳位 ,分別接入 3.3V 。看看是否 print 的 值是否是 4095 。接地是否為 0 
在這裡陣列裡的資料會依序 Channel 0 ,Channel 1 , Channel 4 分別填入,這個就比 Polling 的方式方便了。
原始碼連結


相關連結

ADC 轉換器 - 使用 Polling 多通道輪詢模式

[STM32] ADC 轉換器 - 使用 Polling 多通道輪詢模式

 

STM32 ADC Polling

緣起

我想一開始在做 ADC 轉換這項功能時,要驗證時可能會從 輪詢方式 先著手吧。因為第一印象通常是比較容易達成,確實也是。實作時很快就有結果,拿著官方範例一步一步做好。官方是使用單通道做為基本範例。可是當時我的需求是多通道,經過一番測試仍然測試不出來。迫於時間因素就先改弦易轍,換了 ADC DMA 模式,其實也沒花太多時間研究也就實做出來。雖然,輪詢模式缺點是比較耗 MCU 資源,但是在心中覺得還想試試看放在心中的一個疑問。

基本設定


這次使用 NUCLEO-F401RE 開發版測試,執行 STM32CubeIDE 後直接進入 ADC1 設定項目,選擇其中 3 個 ADC Input 。

IN0    ->    PA0
IN1    ->    PA1
IN4    ->    PA4

STM32 ADC

Parameter Settings 標籤

Resoltion 維持 12 Bits 解析能力,也就是 0 ~ 4095 範圍。

Scan Conversion Mode 設定 Enabled 

Continuous Conversion Mode 設定 Enabled

Number Of Conversion 選擇 3 ,表示有 3 組ADC Iinput

Rank 要各自選擇 Channel

其他設定項目維持 Default 值。
STM32 ADC


儲存後 ,由 STM32CubeIDE 產生程式碼。


程式碼


打開 main.c 後,首先建立 readVoltage() function


uint16_t readVoltage()
{
       HAL_ADC_Start(&hadc1); /* 啟動 ADC 轉換*/
       /*     等待轉換完成 , 100 指 timout 時間。 單位 ms*/
       if(HAL_ADC_PollForConversion(&hadc1,100) == HAL_OK)
       {
              /*     確認轉換完成 Flag */
              if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),HAL_ADC_STATE_REG_EOC))
              {
                     return (uint16_t)HAL_ADC_GetValue(&hadc1); /*讀取轉換數值 0 ~ 4095 之間*/
              }
       }
       return 0;
}
建立 2 個 Value
  uint8_t x;
  uint16_t ADValue[3];

到主 while 迴圈中

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
        HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
        ADValue[x] = readVoltage();
        printf("idx[%d] value[%d]\r\n",x,ADValue[x] );
        x++;
        if(x >=3)
        {
               x = 0;
               printf("\r\n");
        }
         HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

readVoltage() 讀到值後,會將 3 組 ADC Input 值儲存在 ADValue[3] 陣列裡。

好了,這樣就完成了。

簡易的測試方法是將剛剛設定的 ADC 腳位 ,分別接入 3.3V 。看看是否 print 的 值是否是接近 4095 。接地是否為 0 

不過,遇到一個不解的地方。以為 Polling 讀 ADC 值的時候,會按照 Channel 0 ,Channel 1 , Channel 4 按照這個順序。結果並不是,所以需要一個一個實際確認對應的 ADC Input 腳位及陣列的對應索引。這個問題不大,實務上本來就是需要各自確認 ADC 轉換有沒有工作正常。

原始碼連結
https://github.com/cold63/STM32_Code/tree/master/ADC_Polling



相關連結

ADC 轉換器 - 使用 DMA 多通道模式

2021年8月21日星期六

[MicroPython] STM32 + MicroPython + SSD1306 基本使用

 今天來測試 SSD1306 OLED 顯示模組,SSD1306 是透過 I2C 來控制。所以在我們 DIY Pyboard 連接 VCC,GND,SCL,SDA。為了使用麵包板方便連接,我使用 PB3 (SDA) ,PB10(SCL) 這一組來使用。以這次設計的 板子架構來說是 第 2 組 I2C。


準備

STM32_MiniBoard x 1
SSD1306                 x 1
麵包板                    x 1
若干杜邦線


接線

MicroPython



開始

在 MicroPython 裡已經有 SSD1306 驅動程式,在源碼 Drivers/display 目錄裡。不過,在這我自行編譯的 MicroPython firmware 已經加入 SSD1306 驅動,所以不用另外拷貝出來使用。首先我們將 開啟 Thonny 並且開啟 REPL 提示列。

輸入以下程式段

from machine import Pin,I2C
i2c = I2C(2)
I2C(2) 是指 第二組 I2C 通訊

我們可以測試一下
i2c.scan()

如果沒有問題,會顯示以下結果。




表示 SSD1306 的位置是 60 (為10進制, 16進制為 0x3C) ,且 I2C 接線無誤。
繼續輸入以下程式段

from ssd1306 import SSD1306_I2C
oled = SSD1306_I2C(128, 64, i2c)
oled.text("hello makdev.net",0,0)
oled.show()
實際畫面是
MicroPython













我們接著試著使用 Raspberry Pi Pico  RP2040 的 範例測試一下 source code 連結 (https://github.com/raspberrypi/pico-micropython-examples/tree/master/i2c/1306oled/i2c_1306oled_using_defaults.py)

源碼的 第 9 行 



i2c = I2C(0)
改為

i2c = I2C(2)
在 Thonny 中,使用檔案開啟功能 開啟 MicroPython Device 的方式,開啟 main.py 並將全部 Sorce code 貼上並儲存。



# Display Image & text on I2C driven ssd1306 OLED display
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import framebuf

WIDTH  = 128                                            # oled display width
HEIGHT = 32                                             # oled display height

i2c = I2C(2)                                            # Init I2C using I2C0 defaults, SCL=Pin(GP9), SDA=Pin(GP8), freq=400000
print("I2C Address      : "+hex(i2c.scan()[0]).upper()) # Display device address
print("I2C Configuration: "+str(i2c))                   # Display I2C config


oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)                  # Init oled display

# Raspberry Pi logo as 32x32 bytearray
buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")

# Load the raspberry pi logo into the framebuffer (the image is 32x32)
fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB)

# Clear the oled display in case it has junk on it.
oled.fill(0)

# Blit the image from the framebuffer to the oled display
oled.blit(fb, 96, 0)

# Add some text
oled.text("Raspberry Pi",5,5)
oled.text("Pico",5,15)

# Finally update the oled display so the image & text is displayed
oled.show()
點擊 Run 按鈕
MicroPython



畫面變成
MicroPython






相關文章

開始第一個 micropython 程式
STM32F4 使用 MicroPython 應用
如何安裝 PyBoard 的 Com Port


2021年8月8日星期日

[MicroPython] 開始第一個 micropython 程式

拿到 STM32F401_MiniPyboard 之後就想先測試一下 MicroPython 簡單的程式,順便驗證一下板子。開始之前 先預安裝 Thonny  IDE ,這是編輯程式 及 上傳程式的工具。它同時支援 Windows , Linux 及 MAC 系統。

安裝程式

可以在這裡下載安裝 (https://thonny.org/) , 安裝方式依序按下一步安裝即可。


執行 Thonnny IDE


Thonny


設定 Thonny IDE

先將 STM32F401_MiniPyboard 接入 USB cable。
在 Thonny IDE 的選單點開 Run ->Select interpreter...  並選擇 Interpreter Tab。

選擇 MicroPython(generic)

MicroPython


選好後 下方會出現 Port 的選擇 , 選擇剛接入開發板後 新增的 Com port。 然後點下方的 OK

MicroPython



回到主畫面後 , 在 Shell 窗口 是 REPL 互動窗口。會顯示以下

MicroPython



表示到現在已經正常連接了。
我們可以在 Shell / REPL 窗口直接編輯程式。

MicroPython

依序往下編輯, 這時候 LED 應該是會閃爍了。


如果要開發板上電之後開始執行程式?


點選單 File -> Open ...



選擇 MicroPython device




選擇 main.py 開啟,並將剛剛的 程式 填入




儲存後 ,在 Shell 按組合鍵 Ctrl + D 軟啟動。或是 重新上電

這時候 LED 應該會開始閃爍。