痞酷網_PIGOO

 找回密碼
 立即註冊
!!! [系統偵測到廣告阻擋軟體] !!!

如果您覺得痞酷網對您有些許幫助,或者您認同痞酷網的理想,

那麼希望您將痞酷網設定為白名單.

並請在上論壇的時候,動動您的手指,用行動支持我們.

謝謝!
查看: 16317|回復: 30

[實做與討論] RMS 交流錶頭試作,第一版

[複製鏈接]
發表於 2020-3-30 20:43:11 | 顯示全部樓層 |閱讀模式
這篇呢,我打算分幾次寫,把樓蓋高一點,不會在一樓就直接把它寫完了。也就是說,雖然是很簡單的小東西,但是我故意將它複雜化了。我想,這裡應該需要一點這樣的東西……

在開樓的此時,要做的這東西其實基本都已經完成了,只剩最後一段切換輸入輸出範圍的程式碼還沒有開始除錯。

源起:因為某些原因,我需要做一個治具,也許未來會介紹,也許不會。什麼東西?某個交流信號處理一下之後再送出去,就這樣子而已,很簡單吧!那我想在送出信號之前,知道它的 RMS 值。這本來應該是很簡單的事,用電錶在輸出端量一下就好了,但我想在機殼上裝一個錶頭直接讀取它的 RMS 值。所以呢,我先找了現成的東西……

IMG_20200330_193445.jpg

很可惜,這是直流錶頭,只能讀取直流電壓值。為了要讓它能夠使用,所以必需要加裝一個 RMS 計算器,現在這個很方便,都已經 IC 化了。那我就去找了,結果很快的決定另外尋找替代方案。主要原因有二,第一現在特硃時期交通運輸出現障礙,我不能等太久;第二,這種 IC 的標價,會讓大部分人尿褲子……

再去找看看有沒有現成的 RMS 交流電壓錶頭……

噗噗!

當然沒有!!我想就算是有, 恐怕那價格也不會漂亮到哪裡去吧!

所以決定使用數位軟體方案。這有兩種選擇,一種是使用專門的 meter IC,不過這似乎不太適合我的場合,再者還是需要有一顆 MCU 來控制,那何不直接使用 MCU 來算就好了?事實上也是有這種 MCU 的分類。所以決定使用純 MCU 方案。但這需要使用到計算力有一點強的 MCU 才行……考量到需要處理大量的數據,8 位元的 MCU 恐怕是力有未逮……這最多只能拿來控制操作 meter IC 而已。現在 32 位元的 ARM 來勢洶洶有一統天下的趨勢。所以決定選用 STM32 來做這事情。

翻了一下手邊的箱子,之前買的 STM32F030F4P6 最小系統板,想來 STM32 我還沒有正式用過呢!就拿這片來試試,我這許久未開封的老刀……

IMG_20200330_193617.jpg

根據這 IC 的能力,內建 12 位的 A/D,最快為 1MSPS,初步訂下的 spec 為頻寬 1KHz,RMS 計算力,每秒更新十次。其實做到 100KHz 應該是可以……不過一開始東西未熟練之前,規格還是先訂得保守一點,先求有再求好。

因為 A/D 是 12 位,所以最多只能做到 4 位顯示輸出,足矣!這規格已經是吊打市面上所有的山寨模組了!

現在要使用這個最小系統板來做一個簡單的 RMS 錶頭。

這篇寫完之後過一陣子看看市面上會不會出現一大堆便宜的 RMS 交流錶頭……站長如果有興趣的話可以做來放在站上專賣,基本上我算是很滿意這錶頭的表現呢!

續待。



補充內容 (2020-3-31 08:16 PM):
現在時間 2020/03/31 前夜,第一版的 firmware 已全部完成 release 了,不過我留著慢慢講……這樓有跟帖的我一天回一篇,天天有新文。

評分

21

查看全部評分

發表於 2020-3-30 21:55:16 | 顯示全部樓層
本帖最後由 lazy104 於 2020-3-30 10:41 PM 編輯

這個有趣~

數年前公司曾開發類似UPS的產品, 其中就有偵測RMS的需求, 前輩當時選用了Linear Technology的產品;
後期整份粗估電子零件表列出來, 就它一顆八腳的IC鶴立雞群, 單價要三位數台幣, 還真讓人快尿褲子...

發表於 2020-3-30 22:22:15 | 顯示全部樓層
若能上youtube更好
發表於 2020-3-31 09:11:34 | 顯示全部樓層
RMS 畢竟是小眾市場只有少數內行人才有需求,對岸那邊不是做不出來而是沒有多少人要買。

先前也有想做一個簡單的 RMS 轉換電路,後來想想我要測的信號是固定頻率的正弦波信號,用精密交流轉換電路就解決了。

建議還是要完整的測一下交流響應能到幾 KHz,很好奇這類 MCU 的能力。
繼續關注下文!
 樓主| 發表於 2020-3-31 16:18:24 | 顯示全部樓層
先來決定硬體接線的方式。

IMG_20200331_151850.jpg

我喜歡直接用手繪,除非是決定要 layout,不然其實我是不太去開 CAD 的。

經過修修改改多次,應該是到此定案不會再改了。不過這一版預定只做一次,下一版如果還要做的話我會換個有更多隻腳的 MCU……

本來是打算只用一個 STM32F030F4P6 完成所有的工作。到最後發現 I/O 不夠用,真是糗大了!頭都洗到一半了怎麼能半途而廢!只好再加一顆 74HC595,加上一顆參考源 TL431,所以呢總共有三顆IC。實際上因為 MCU 輸入不能是負電壓,所以在量測交流信號時還要再加一組精密整流器。這個圖上沒有畫出來,這部分的東西請在圖外的地方自行準備。

不要傻傻的像我只用了個 LM324,直流漂移的問題,會讓你調整一堆 VR 調到懷疑人生……

VREF 使用了外部的參考源,使用 TL431,或是更好的,定在 3.2V。

PA0 做為 A/D 輸入類比信號,最小輸入 0V,最大輸入為 VREF。

時鐘使用了外部的 8MHz OSC,內部 PLL 會調到 48MHz 給 CPU 使用,TIMER 為 24MHz(HCLK),其它的週邊設定為 12MHz(PCLK)。

顯示器使用了四顆共陽七段LED,搭配 595 使用,總共用掉了 MCU 的 7 條接腳。

PA9/10/13/14 四條腳是供開發與除錯使用,此時為必需保留的資源,暫時沒辦法空出來。

最後一個 PB1 是給外部的類比開關,作為切換輸入信號準位用的。輸出 0 時 x1 的 dp 會亮,到 3000 以上時切到輸出 1,此時 x100 的 dp 會亮,此時 299 以下時會切回 0。PA1 輸入範圍 0~3.2V,每 1mVRMS 顯示 1。(這部分目前還沒有定案)

IMG_20200331_154051.jpg

手工試作板。

續待。



補充內容 (2020-4-4 12:07 PM):
2020/04/04 硬體修正,TL431 限流電阻由 820Ω 改為 470Ω。
發表於 2020-3-31 16:35:20 | 顯示全部樓層
本帖最後由 goodnight 於 2020-3-31 04:37 PM 編輯

請忽略~~請忽略~請忽略, 不好意思
 樓主| 發表於 2020-3-31 20:21:01 | 顯示全部樓層

不過小弟更樂於做一位路人甲呢……

發表於 2020-3-31 22:06:51 | 顯示全部樓層
有跟帖,天天有新帖~
發表於 2020-3-31 22:19:02 | 顯示全部樓層
以前幫客戶設計生產過一種儀器,必須測量交流訊號RMS值,為了省事直接使用 AD536 做RMS TO DC轉換,實際表現也很令人滿意。

 樓主| 發表於 2020-4-2 11:02:49 | 顯示全部樓層
硬體線路基本就只有上面一張圖而已。主要的工作都在軟體身上。從現在開始進入這一塊。

compiler 我這裡使用的是 IAR for ARM 7.5,用來開發測試小型程式比較方便,不需要事先設定一大堆東西。

20200402.png

整個專案只有九個檔案。

startup_stmf0xx.s 與 system_stm32f0xx.c 由 ST 提供範例檔,把內容修改一下就好。只要是使用到 STM32F030 並使用 C 語言的話,無論採用哪一種 compiler 基本都會碰用到這兩個 (以上) 檔案。

StdRead.c 與 StdWrite.c 修改自 IAR 提供的範例檔,如果是用 keil 或 gcc 之類的,請自行修改或刪除這部分的東西。

在 IAR 下面開一個空的 ARM 專案,選好對應的 MCU 編號,把所有的檔案丟進去就完成了。

續待。

 樓主| 發表於 2020-4-4 12:18:38 | 顯示全部樓層
程式專案內容不丟懶人包,請自行慢慢收集……

原本找不到 startup_stm32f0xx.s,所以抓了另一個檔案改一改。

startup_stm32f0xx.s 內容,RESET 後的第一行以及中斷向量表:
  1. ;/******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
  2. ;* File Name          : startup_stm32f40x.s
  3. ;* Author             : MCD Application Team
  4. ;* Version            : V0.0.2
  5. ;* Date               : 25-July-2011
  6. ;* Description        : STM32F40x devices vector table for EWARM toolchain.
  7. ;*                      This module performs:
  8. ;*                      - Set the initial SP
  9. ;*                      - Set the initial PC == _iar_program_start,
  10. ;*                      - Set the vector table entries with the exceptions ISR
  11. ;*                        address.
  12. ;*                      After Reset the Cortex-M4 processor is in Thread mode,
  13. ;*                      priority is Privileged, and the Stack is set to Main.
  14. ;********************************************************************************
  15. ;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  16. ;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
  17. ;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
  18. ;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
  19. ;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
  20. ;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  21. ;*******************************************************************************/
  22. ;
  23. ;
  24. ; The modules in this file are included in the libraries, and may be replaced
  25. ; by any user-defined modules that define the PUBLIC symbol _program_start or
  26. ; a user defined start symbol.
  27. ; To override the cstartup defined in the library, simply add your modified
  28. ; version to the workbench project.
  29. ;
  30. ; The vector table is normally located at address 0.
  31. ; When debugging in RAM, it can be located in RAM, aligned to at least 2^6.
  32. ; The name "__vector_table" has special meaning for C-SPY:
  33. ; it is where the SP start value is found, and the NVIC vector
  34. ; table register (VTOR) is initialized to this address if != 0.
  35. ;
  36. ; Cortex-M version
  37. ;

  38.         MODULE  ?cstartup

  39.         ;; Forward declaration of sections.
  40.         SECTION CSTACK:DATA:NOROOT(3)

  41.         SECTION .intvec:CODE:NOROOT(2)

  42.         EXTERN  __iar_program_start
  43.         EXTWEAK __iar_init_vfp
  44.         EXTERN  SystemInit
  45.         PUBLIC  __vector_table

  46.         DATA
  47. __vector_table
  48.         DCD     sfe(CSTACK)                                        ; Reserved                                                                @ 0x0000 0000
  49.         DCD     Reset_Handler                                ; Reset Handler                                                        @ 0x0000 0004
  50.         DCD     NMI_Handler                                        ; NMI Handler                                                        @ 0x0000 0008
  51.         DCD     HardFault_Handler                        ; Hard Fault Handler                                        @ 0x0000 000c

  52.         DCD     MemManage_Handler                        ; Reserved                                                                @ 0x0000 0010
  53.         DCD     BusFault_Handler                        ; Reserved                                                                @ 0x0000 0014
  54.         DCD     UsageFault_Handler                        ; Reserved                                                                @ 0x0000 0018
  55.         DCD     0                                                        ; Reserved                                                                @ 0x0000 001c

  56.                 DCD     0                                                        ; Reserved                                                                @ 0x0000 0020
  57.         DCD     0                                                        ; Reserved                                                                @ 0x0000 0024
  58.         DCD     0                                                        ; Reserved                                                                @ 0x0000 0028
  59.         DCD     SVC_Handler                                        ; SVCall Handler                                                @ 0x0000 002c

  60.                 DCD     DebugMon_Handler                        ; Reserved                                                                @ 0x0000 0030
  61.         DCD     0                                                        ; Reserved                                                                @ 0x0000 0034
  62.         DCD     PendSV_Handler                                ; PendSV Handler                                                @ 0x0000 0038
  63.         DCD     SysTick_Handler                                ; SysTick Handler                                                @ 0x0000 003c

  64.          ; External Interrupts
  65.         DCD     WWDG_IRQHandler                                ; Window WatchDog                                                @ 0x0000 0040
  66.         DCD     0                                                        ; Reserved
  67.         DCD     RTC_WKUP_IRQHandler                        ; RTC Wakeup through the EXTI line
  68.         DCD     FLASH_IRQHandler                        ; FLASH
  69.         DCD     RCC_IRQHandler                                ; RCC
  70.         DCD     EXTI0_1_IRQHandler                        ; EXTI Line[1:0]
  71.         DCD     EXTI2_3_IRQHandler                        ; EXTI Line[3:2]
  72.         DCD     EXTI4_15_IRQHandler                        ; EXTI Line[4:15]
  73.         DCD     0                                                        ; Reserved
  74.         DCD     DMA_CH1_IRQHandler                        ; DMA CH1
  75.         DCD     DMA_CH2_3_IRQHandler                ; DMA CH2/3
  76.         DCD     DMA_CH4_5_IRQHandler                ; DMA CH4/5
  77.         DCD     ADC_IRQHandler                                ; ADC
  78.                 DCD     TIM1_BRK_UP_TRG_COM_IRQHandler
  79.         DCD     TIM1_CC_IRQHandler                        ; TIM1
  80.                 DCD     0                                                        ; Reserved
  81.                 DCD                TIM3_IRQHandler                                ; TIM3
  82.         DCD     TIM6_IRQHandler                                ; TIM6
  83.         DCD     0                                                        ; Reserved
  84.                 DCD                TIM14_IRQHandler
  85.                 DCD                TIM15_IRQHandler
  86.                 DCD                TIM16_IRQHandler
  87.                 DCD                TIM17_IRQHandler
  88.                 DCD                I2C1_IRQHandler
  89.                 DCD                I2C2_IRQHandler
  90.                 DCD                SPI1_IRQHandler
  91.                 DCD                SPI2_IRQHandler
  92.                 DCD                USART1_IRQHandler
  93.                 DCD                USART2_IRQHandler
  94.                 DCD                USART3_4_5_6_IRQHandler
  95.                 DCD                0
  96.                 DCD                USB_IRQHandler

  97. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  98. ;;
  99. ;; Default interrupt handlers.
  100. ;;
  101.         THUMB
  102.         PUBWEAK Reset_Handler
  103.         SECTION .text:CODE:REORDER:NOROOT(2)
  104. Reset_Handler
  105.         FUNCALL Reset_Handler, __iar_init_vfp   ; Enable VFP if it is enabled in the project settings
  106.         BL      __iar_init_vfp

  107.         LDR     R0, =SystemInit
  108.         BLX     R0

  109.         LDR     R0, =__iar_program_start
  110.         BX      R0

  111.         PUBWEAK NMI_Handler
  112.         SECTION .text:CODE:REORDER:NOROOT(1)
  113. NMI_Handler
  114.         B NMI_Handler

  115.         PUBWEAK HardFault_Handler
  116.         SECTION .text:CODE:REORDER:NOROOT(1)
  117. HardFault_Handler
  118.         B HardFault_Handler

  119.         PUBWEAK MemManage_Handler
  120.         SECTION .text:CODE:REORDER:NOROOT(1)
  121. MemManage_Handler
  122.         B MemManage_Handler

  123.         PUBWEAK BusFault_Handler
  124.         SECTION .text:CODE:REORDER:NOROOT(1)
  125. BusFault_Handler
  126.         B BusFault_Handler

  127.         PUBWEAK UsageFault_Handler
  128.         SECTION .text:CODE:REORDER:NOROOT(1)
  129. UsageFault_Handler
  130.         B UsageFault_Handler

  131.         PUBWEAK SVC_Handler
  132.         SECTION .text:CODE:REORDER:NOROOT(1)
  133. SVC_Handler
  134.         B SVC_Handler

  135.         PUBWEAK DebugMon_Handler
  136.         SECTION .text:CODE:REORDER:NOROOT(1)
  137. DebugMon_Handler
  138.         B DebugMon_Handler

  139.         PUBWEAK PendSV_Handler
  140.         SECTION .text:CODE:REORDER:NOROOT(1)
  141. PendSV_Handler
  142.         B PendSV_Handler

  143.         PUBWEAK SysTick_Handler
  144.         SECTION .text:CODE:REORDER:NOROOT(1)
  145. SysTick_Handler
  146.         B SysTick_Handler

  147. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  148. ;;
  149. ;; peripheral interrupt handlers.
  150. ;;

  151.         PUBWEAK WWDG_IRQHandler
  152.         SECTION .text:CODE:REORDER:NOROOT(1)
  153. WWDG_IRQHandler
  154.         B WWDG_IRQHandler

  155.         PUBWEAK RTC_WKUP_IRQHandler
  156.         SECTION .text:CODE:REORDER:NOROOT(1)
  157. RTC_WKUP_IRQHandler
  158.         B RTC_WKUP_IRQHandler

  159.         PUBWEAK FLASH_IRQHandler
  160.         SECTION .text:CODE:REORDER:NOROOT(1)
  161. FLASH_IRQHandler
  162.         B FLASH_IRQHandler

  163.         PUBWEAK RCC_IRQHandler
  164.         SECTION .text:CODE:REORDER:NOROOT(1)
  165. RCC_IRQHandler
  166.         B RCC_IRQHandler

  167.         PUBWEAK EXTI0_1_IRQHandler
  168.         SECTION .text:CODE:REORDER:NOROOT(1)
  169. EXTI0_1_IRQHandler
  170.         B EXTI0_1_IRQHandler

  171.         PUBWEAK EXTI2_3_IRQHandler
  172.         SECTION .text:CODE:REORDER:NOROOT(1)
  173. EXTI2_3_IRQHandler
  174.         B EXTI2_3_IRQHandler

  175.         PUBWEAK EXTI4_15_IRQHandler
  176.         SECTION .text:CODE:REORDER:NOROOT(1)
  177. EXTI4_15_IRQHandler
  178.         B EXTI4_15_IRQHandler

  179.         PUBWEAK DMA_CH1_IRQHandler
  180.         SECTION .text:CODE:REORDER:NOROOT(1)
  181. DMA_CH1_IRQHandler
  182.         B DMA_CH1_IRQHandler

  183.         PUBWEAK DMA_CH2_3_IRQHandler
  184.         SECTION .text:CODE:REORDER:NOROOT(1)
  185. DMA_CH2_3_IRQHandler
  186.         B DMA_CH2_3_IRQHandler

  187.         PUBWEAK DMA_CH4_5_IRQHandler
  188.         SECTION .text:CODE:REORDER:NOROOT(1)
  189. DMA_CH4_5_IRQHandler
  190.         B DMA_CH4_5_IRQHandler

  191.         PUBWEAK ADC_IRQHandler
  192.         SECTION .text:CODE:REORDER:NOROOT(1)
  193. ADC_IRQHandler
  194.         B ADC_IRQHandler

  195.         PUBWEAK TIM1_BRK_UP_TRG_COM_IRQHandler
  196.         SECTION .text:CODE:REORDER:NOROOT(1)
  197. TIM1_BRK_UP_TRG_COM_IRQHandler
  198.         B TIM1_BRK_UP_TRG_COM_IRQHandler

  199.         PUBWEAK TIM1_CC_IRQHandler
  200.         SECTION .text:CODE:REORDER:NOROOT(1)
  201. TIM1_CC_IRQHandler
  202.         B TIM1_CC_IRQHandler

  203.         PUBWEAK TIM3_IRQHandler
  204.         SECTION .text:CODE:REORDER:NOROOT(1)
  205. TIM3_IRQHandler
  206.         B TIM3_IRQHandler

  207.         PUBWEAK TIM6_IRQHandler
  208.         SECTION .text:CODE:REORDER:NOROOT(1)
  209. TIM6_IRQHandler
  210.         B TIM6_IRQHandler

  211.         PUBWEAK TIM14_IRQHandler
  212.         SECTION .text:CODE:REORDER:NOROOT(1)
  213. TIM14_IRQHandler
  214.         B TIM14_IRQHandler

  215.         PUBWEAK TIM15_IRQHandler
  216.         SECTION .text:CODE:REORDER:NOROOT(1)
  217. TIM15_IRQHandler
  218.         B TIM15_IRQHandler

  219.         PUBWEAK TIM16_IRQHandler
  220.         SECTION .text:CODE:REORDER:NOROOT(1)
  221. TIM16_IRQHandler
  222.         B TIM16_IRQHandler

  223.         PUBWEAK TIM17_IRQHandler
  224.         SECTION .text:CODE:REORDER:NOROOT(1)
  225. TIM17_IRQHandler
  226.         B TIM17_IRQHandler

  227.         PUBWEAK I2C1_IRQHandler
  228.         SECTION .text:CODE:REORDER:NOROOT(1)
  229. I2C1_IRQHandler
  230.         B I2C1_IRQHandler

  231.         PUBWEAK I2C2_IRQHandler
  232.         SECTION .text:CODE:REORDER:NOROOT(1)
  233. I2C2_IRQHandler
  234.         B I2C2_IRQHandler

  235.         PUBWEAK SPI1_IRQHandler
  236.         SECTION .text:CODE:REORDER:NOROOT(1)
  237. SPI1_IRQHandler
  238.         B SPI1_IRQHandler

  239.         PUBWEAK SPI2_IRQHandler
  240.         SECTION .text:CODE:REORDER:NOROOT(1)
  241. SPI2_IRQHandler
  242.         B SPI2_IRQHandler

  243.         PUBWEAK USART1_IRQHandler
  244.         SECTION .text:CODE:REORDER:NOROOT(1)
  245. USART1_IRQHandler
  246.         B USART1_IRQHandler

  247.         PUBWEAK USART2_IRQHandler
  248.         SECTION .text:CODE:REORDER:NOROOT(1)
  249. USART2_IRQHandler
  250.         B USART2_IRQHandler

  251.         PUBWEAK USART3_4_5_6_IRQHandler
  252.         SECTION .text:CODE:REORDER:NOROOT(1)
  253. USART3_4_5_6_IRQHandler
  254.         B USART3_4_5_6_IRQHandler

  255.         PUBWEAK USB_IRQHandler
  256.         SECTION .text:CODE:REORDER:NOROOT(1)
  257. USB_IRQHandler
  258.         B USB_IRQHandler

  259.         END
  260. /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
複製代碼



system_stm32f0xx.c:

這個檔案可有可無,主要是在 startup_stm32f0xx.s 裡有一個 call  SystemInit() 的動作,裡面其實也沒有什麼,直接註解掉也是可以跑。

  1. /**

  2.   ******************************************************************************

  3.   * @file    system_stm32f0xx.c

  4.   * @author  MCD Application Team

  5.   * @brief   CMSIS Cortex-M0 Device Peripheral Access Layer System Source File.

  6.   *

  7.   * 1. This file provides two functions and one global variable to be called from

  8.   *    user application:

  9.   *      - SystemInit(): This function is called at startup just after reset and

  10.   *                      before branch to main program. This call is made inside

  11.   *                      the "startup_stm32f0xx.s" file.

  12.   *

  13.   *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used

  14.   *                                  by the user application to setup the SysTick

  15.   *                                  timer or configure other parameters.

  16.   *

  17.   *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must

  18.   *                                 be called whenever the core clock is changed

  19.   *                                 during program execution.

  20.   *

  21.   * 2. After each device reset the HSI (8 MHz) is used as system clock source.

  22.   *    Then SystemInit() function is called, in "startup_stm32f0xx.s" file, to

  23.   *    configure the system clock before to branch to main program.

  24.   *

  25.   * 3. This file configures the system clock as follows:

  26.   *=============================================================================

  27.   *                         Supported STM32F0xx device

  28.   *-----------------------------------------------------------------------------

  29.   *        System Clock source                    | HSI

  30.   *-----------------------------------------------------------------------------

  31.   *        SYSCLK(Hz)                             | 8000000

  32.   *-----------------------------------------------------------------------------

  33.   *        HCLK(Hz)                               | 8000000

  34.   *-----------------------------------------------------------------------------

  35.   *        AHB Prescaler                          | 1

  36.   *-----------------------------------------------------------------------------

  37.   *        APB1 Prescaler                         | 1

  38.   *-----------------------------------------------------------------------------

  39.   *=============================================================================

  40.   ******************************************************************************

  41.   * @attention

  42.   *

  43.   * <h2><center>© Copyright (c) 2016 STMicroelectronics.

  44.   * All rights reserved.</center></h2>

  45.   *

  46.   * This software component is licensed by ST under BSD 3-Clause license,

  47.   * the "License"; You may not use this file except in compliance with the

  48.   * License. You may obtain a copy of the License at:

  49.   *                        opensource.org/licenses/BSD-3-Clause

  50.   *

  51.   ******************************************************************************

  52.   */



  53. /** @addtogroup CMSIS

  54.   * @{

  55.   */



  56. /** @addtogroup stm32f0xx_system

  57.   * @{

  58.   */



  59. /** @addtogroup STM32F0xx_System_Private_Includes

  60.   * @{

  61.   */



  62. //#include "stm32f0xx.h"
  63. #include <ST/iostm32f051x4.h>



  64. /**

  65.   * @}

  66.   */



  67. /** @addtogroup STM32F0xx_System_Private_TypesDefinitions

  68.   * @{

  69.   */



  70. /**

  71.   * @}

  72.   */



  73. /** @addtogroup STM32F0xx_System_Private_Defines

  74.   * @{

  75.   */

  76. #if !defined  (HSE_VALUE)

  77.   #define HSE_VALUE    ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz.

  78.                                                 This value can be provided and adapted by the user application. */

  79. #endif /* HSE_VALUE */



  80. #if !defined  (HSI_VALUE)

  81.   #define HSI_VALUE    ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz.

  82.                                                 This value can be provided and adapted by the user application. */

  83. #endif /* HSI_VALUE */



  84. #if !defined (HSI48_VALUE)

  85. #define HSI48_VALUE    ((uint32_t)48000000) /*!< Default value of the HSI48 Internal oscillator in Hz.

  86.                                                  This value can be provided and adapted by the user application. */

  87. #endif /* HSI48_VALUE */

  88. /**

  89.   * @}

  90.   */



  91. /** @addtogroup STM32F0xx_System_Private_Macros

  92.   * @{

  93.   */



  94. /**

  95.   * @}

  96.   */



  97. /** @addtogroup STM32F0xx_System_Private_Variables

  98.   * @{

  99.   */

  100.   /* This variable is updated in three ways:

  101.       1) by calling CMSIS function SystemCoreClockUpdate()

  102.       2) by calling HAL API function HAL_RCC_GetHCLKFreq()

  103.       3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency

  104.          Note: If you use this function to configure the system clock there is no need to

  105.                call the 2 first functions listed above, since SystemCoreClock variable is

  106.                updated automatically.

  107.   */

  108. //uint32_t SystemCoreClock = 8000000;



  109. //const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

  110. //const uint8_t APBPrescTable[8]  = {0, 0, 0, 0, 1, 2, 3, 4};



  111. /**

  112.   * @}

  113.   */



  114. /** @addtogroup STM32F0xx_System_Private_FunctionPrototypes

  115.   * @{

  116.   */



  117. /**

  118.   * @}

  119.   */



  120. /** @addtogroup STM32F0xx_System_Private_Functions

  121.   * @{

  122.   */



  123. /**

  124.   * @brief  Setup the microcontroller system.

  125.   *         Initialize the default HSI clock source, vector table location and the PLL configuration is reset.

  126.   * @param  None

  127.   * @retval None

  128.   */

  129. #ifndef uint32_t
  130. typedef unsigned int                uint32_t;
  131. #endif

  132. void SystemInit(void)

  133. {

  134.   /* Reset the RCC clock configuration to the default reset state ------------*/

  135.   /* Set HSION bit */

  136.   RCC_CR |= (uint32_t)0x00000001U;



  137. #if defined (STM32F051x8) || defined (STM32F058x8)

  138.   /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */

  139.   RCC_CFGR &= (uint32_t)0xF8FFB80CU;

  140. #else

  141.   /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */

  142.   RCC_CFGR &= (uint32_t)0x08FFB80CU;

  143. #endif /* STM32F051x8 or STM32F058x8 */

  144.   

  145.   /* Reset HSEON, CSSON and PLLON bits */

  146.   RCC_CR &= (uint32_t)0xFEF6FFFFU;



  147.   /* Reset HSEBYP bit */

  148.   RCC_CR &= (uint32_t)0xFFFBFFFFU;



  149.   /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */

  150.   RCC_CFGR &= (uint32_t)0xFFC0FFFFU;



  151.   /* Reset PREDIV[3:0] bits */

  152.   RCC_CFGR2 &= (uint32_t)0xFFFFFFF0U;



  153. #if defined (STM32F072xB) || defined (STM32F078xx)

  154.   /* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */

  155.   RCC->CFGR3 &= (uint32_t)0xFFFCFE2CU;

  156. #elif defined (STM32F071xB)

  157.   /* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */

  158.   RCC->CFGR3 &= (uint32_t)0xFFFFCEACU;

  159. #elif defined (STM32F091xC) || defined (STM32F098xx)

  160.   /* Reset USART3SW[1:0], USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */

  161.   RCC_CFGR3 &= (uint32_t)0xFFF0FEACU;

  162. #elif defined (STM32F030x6) || defined (STM32F030x8) || defined (STM32F031x6) || defined (STM32F038xx) || defined (STM32F030xC)

  163.   /* Reset USART1SW[1:0], I2C1SW and ADCSW bits */

  164.   RCC_CFGR3 &= (uint32_t)0xFFFFFEECU;

  165. #elif defined (STM32F051x8) || defined (STM32F058xx)

  166.   /* Reset USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */

  167.   RCC_CFGR3 &= (uint32_t)0xFFFFFEACU;

  168. #elif defined (STM32F042x6) || defined (STM32F048xx)

  169.   /* Reset USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */

  170.   RCC_CFGR3 &= (uint32_t)0xFFFFFE2CU;

  171. #elif defined (STM32F070x6) || defined (STM32F070xB)

  172.   /* Reset USART1SW[1:0], I2C1SW, USBSW and ADCSW bits */

  173.   RCC_CFGR3 &= (uint32_t)0xFFFFFE6CU;

  174.   /* Set default USB clock to PLLCLK, since there is no HSI48 */

  175.   RCC_CFGR3 |= (uint32_t)0x00000080U;  

  176. #else

  177.   RCC_CFGR3 = (uint32_t)0;
  178. // #warning "No target selected"

  179. #endif



  180.   /* Reset HSI14 bit */

  181.   RCC_CR2 &= (uint32_t)0xFFFFFFFEU;



  182.   /* Disable all interrupts */

  183.   RCC_CIR = 0x00000000U;



  184. }


  185. #if 0


  186. /**

  187.    * @brief  Update SystemCoreClock variable according to Clock Register Values.

  188.   *         The SystemCoreClock variable contains the core clock (HCLK), it can

  189.   *         be used by the user application to setup the SysTick timer or configure

  190.   *         other parameters.

  191.   *

  192.   * @note   Each time the core clock (HCLK) changes, this function must be called

  193.   *         to update SystemCoreClock variable value. Otherwise, any configuration

  194.   *         based on this variable will be incorrect.

  195.   *

  196.   * @note   - The system frequency computed by this function is not the real

  197.   *           frequency in the chip. It is calculated based on the predefined

  198.   *           constant and the selected clock source:

  199.   *

  200.   *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)

  201.   *

  202.   *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)

  203.   *

  204.   *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)

  205.   *             or HSI_VALUE(*) multiplied/divided by the PLL factors.

  206.   *

  207.   *         (*) HSI_VALUE is a constant defined in stm32f0xx_hal.h file (default value

  208.   *             8 MHz) but the real value may vary depending on the variations

  209.   *             in voltage and temperature.

  210.   *

  211.   *         (**) HSE_VALUE is a constant defined in stm32f0xx_hal.h file (default value

  212.   *              8 MHz), user has to ensure that HSE_VALUE is same as the real

  213.   *              frequency of the crystal used. Otherwise, this function may

  214.   *              have wrong result.

  215.   *

  216.   *         - The result of this function could be not correct when using fractional

  217.   *           value for HSE crystal.

  218.   *

  219.   * @param  None

  220.   * @retval None

  221.   */

  222. void SystemCoreClockUpdate (void)

  223. {

  224.   uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0;



  225.   /* Get SYSCLK source -------------------------------------------------------*/

  226.   tmp = RCC->CFGR & RCC_CFGR_SWS;



  227.   switch (tmp)

  228.   {

  229.     case RCC_CFGR_SWS_HSI:  /* HSI used as system clock */

  230.       SystemCoreClock = HSI_VALUE;

  231.       break;

  232.     case RCC_CFGR_SWS_HSE:  /* HSE used as system clock */

  233.       SystemCoreClock = HSE_VALUE;

  234.       break;

  235.     case RCC_CFGR_SWS_PLL:  /* PLL used as system clock */

  236.       /* Get PLL clock source and multiplication factor ----------------------*/

  237.       pllmull = RCC->CFGR & RCC_CFGR_PLLMUL;

  238.       pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;

  239.       pllmull = ( pllmull >> 18) + 2;

  240.       predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1;



  241.       if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV)

  242.       {

  243.         /* HSE used as PLL clock source : SystemCoreClock = HSE/PREDIV * PLLMUL */

  244.         SystemCoreClock = (HSE_VALUE/predivfactor) * pllmull;

  245.       }

  246. #if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F072xB) || defined(STM32F078xx) || defined(STM32F091xC) || defined(STM32F098xx)

  247.       else if (pllsource == RCC_CFGR_PLLSRC_HSI48_PREDIV)

  248.       {

  249.         /* HSI48 used as PLL clock source : SystemCoreClock = HSI48/PREDIV * PLLMUL */

  250.         SystemCoreClock = (HSI48_VALUE/predivfactor) * pllmull;

  251.       }

  252. #endif /* STM32F042x6 || STM32F048xx || STM32F072xB || STM32F078xx || STM32F091xC || STM32F098xx */

  253.       else

  254.       {

  255. #if defined(STM32F042x6) || defined(STM32F048xx)  || defined(STM32F070x6) \

  256. || defined(STM32F078xx) || defined(STM32F071xB)  || defined(STM32F072xB) \

  257. || defined(STM32F070xB) || defined(STM32F091xC) || defined(STM32F098xx)  || defined(STM32F030xC)

  258.         /* HSI used as PLL clock source : SystemCoreClock = HSI/PREDIV * PLLMUL */

  259.         SystemCoreClock = (HSI_VALUE/predivfactor) * pllmull;

  260. #else

  261.         /* HSI used as PLL clock source : SystemCoreClock = HSI/2 * PLLMUL */

  262.         SystemCoreClock = (HSI_VALUE >> 1) * pllmull;

  263. #endif /* STM32F042x6 || STM32F048xx || STM32F070x6 ||

  264.           STM32F071xB || STM32F072xB || STM32F078xx || STM32F070xB ||

  265.           STM32F091xC || STM32F098xx || STM32F030xC */

  266.       }

  267.       break;

  268.     default: /* HSI used as system clock */

  269.       SystemCoreClock = HSI_VALUE;

  270.       break;

  271.   }

  272.   /* Compute HCLK clock frequency ----------------*/

  273.   /* Get HCLK prescaler */

  274.   tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];

  275.   /* HCLK clock frequency */

  276.   SystemCoreClock >>= tmp;

  277. }


  278. #endif


  279. /**

  280.   * @}

  281.   */



  282. /**

  283.   * @}

  284.   */



  285. /**

  286.   * @}

  287.   */



  288. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
複製代碼



評分

2

查看全部評分

 樓主| 發表於 2020-4-6 17:28:41 | 顯示全部樓層
我決定要換掉整流器的 IC 了……

不過這兩天事情多了很多出來,整機完工時程還要再拉長一些。

續主題,再來是 StdRead.c 的內容。這是 IAR 給標準輸出入函式,所謂 STDIN、STDOUT、STDERR 的用戶自訂函式進入點。凡使用到 printf() 這類標準函式都要實作這部分的東西。各家的做法都不一樣。如果不需要的話可以直接註解掉。

StdRead.c:

  1. #ifdef __IAR_SYSTEMS_ICC__

  2. #include <yfuns.h>
  3. #include "myStdio.h"



  4. _STD_BEGIN
  5. #pragma module_name = "?__read"

  6. size_t __read(int handle, unsigned char * buffer, size_t size)
  7. {
  8.         size_t nChars = 0;
  9.         int ret;

  10.         if (handle != _LLIO_STDIN)
  11.         {
  12.                 return _LLIO_ERROR;
  13.         }

  14.         for (/* Empty */; size != 0; --size){
  15.                 ret = UARTGetChar();
  16.                 if(0 <= ret){
  17.                         *buffer = (unsigned char)ret;
  18.                         buffer++;
  19.                 }
  20.                 else{
  21.                         break;
  22.                 }
  23.                 nChars++;
  24.         }

  25.         return nChars;
  26. }




  27. _STD_END

  28. #endif
複製代碼

StdWrite.c:

  1. #ifdef __IAR_SYSTEMS_ICC__

  2. #include <yfuns.h>
  3. #include "myStdio.h"



  4. _STD_BEGIN


  5. #pragma module_name = "?__write"

  6. size_t __write(int handle, const unsigned char * buffer, size_t size)
  7. {
  8.         size_t nChars = 0;
  9.         if (buffer == 0)
  10.         {
  11.                 return 0;
  12.         }
  13.         if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR)
  14.         {
  15.                 return _LLIO_ERROR;
  16.         }
  17.         if(_uart_send_buff_start_pointer >= myStdioBuffSendSize){
  18.                 // Overflow
  19.                 return _LLIO_ERROR;
  20.         }
  21.         for (/* Empty */; size != 0; --size)
  22.         {
  23.                 if (UARTPutChar(*buffer++) < 0)
  24.             {
  25.                         return _LLIO_ERROR;
  26.             }
  27.             ++nChars;
  28.         }
  29.         return nChars;
  30. }

  31. _STD_END

  32. #endif
複製代碼

IAR 不允許將把它們放在同一個檔案內,所以一定要分為多個檔案存放。

 樓主| 發表於 2020-4-13 10:47:44 | 顯示全部樓層
上星期搞得真累……

myStdio.h:
  1. #ifndef myStdio
  2. #define myStdio

  3. #include <stdio.h>

  4. #define myStdioBuffSendSize                256
  5. #define myStdioBuffRecvSize                myStdioBuffSendSize

  6. extern char _uart_send_buff[myStdioBuffSendSize];
  7. extern char _uart_recv_buff[myStdioBuffRecvSize];
  8. extern int _uart_send_buff_end_pointer;
  9. extern int _uart_recv_buff_end_pointer;
  10. extern int _uart_send_buff_start_pointer;
  11. extern int _uart_recv_buff_start_pointer;


  12. int UARTPutChar(char x);
  13. int UARTGetChar(void);
  14. int UARTInit(void);

  15. #endif
複製代碼

myStdio.c:
  1. #include <ST/iostm32f051x4.h>
  2. #include "myStdio.h"
  3. #include "main.h"


  4. char _uart_send_buff[myStdioBuffSendSize];
  5. char _uart_recv_buff[myStdioBuffRecvSize];
  6. int _uart_send_buff_start_pointer = -1;
  7. int _uart_recv_buff_start_pointer = -1;
  8. int _uart_send_buff_end_pointer = -1;
  9. int _uart_recv_buff_end_pointer = -1;


  10. int UARTInit(void)
  11. {
  12.         _uart_send_buff_start_pointer = 0;
  13.         _uart_recv_buff_start_pointer = 0;
  14.         _uart_send_buff_end_pointer = 0;
  15.         _uart_recv_buff_end_pointer = 0;

  16.         USART1_BRR = (USARTDIV & 0xfffffff0) | ((USARTDIV >> 1)& 0x07);
  17.         USART1_CR1_bit.OVER8 = 1;
  18. //        USART1_CR1_bit.TXEIE = 1;                                // Enable Interrupt when TXE=1
  19.         USART1_CR1_bit.RXNEIE = 1;                                // Enable Interrupt when RXENE=1

  20.         USART1_CR1_bit.TE = 1;                                        // Transmitter enable       
  21.         USART1_CR1_bit.RE = 1;                                        // Receiver enable
  22.         USART1_CR1_bit.UE = 1;                                        // USART module enable
  23.        
  24.         USART1_CR3_bit.OVRDIS = 1;                                // Disbale OVERRUN feature

  25.         return 0;
  26. }


  27. int UARTPutChar(char c)
  28. {
  29.         int start_flag = 0;
  30. /*
  31.         if((_uart_send_buff_start_pointer +1) == _uart_send_buff_end_pointer ||
  32.            (_uart_send_buff_start_pointer-255) == _uart_send_buff_end_pointer){
  33.                    // Buff Full
  34.                    return 0;
  35.         }
  36. */
  37.         _uart_send_buff[_uart_send_buff_end_pointer] = c;
  38.         if(_uart_send_buff_start_pointer == _uart_send_buff_end_pointer){
  39.                 start_flag = 1;
  40.         }
  41.         else{
  42.                 start_flag = 0;
  43.         }
  44.        
  45.         if((myStdioBuffSendSize-1) == _uart_send_buff_end_pointer){
  46.                 _uart_send_buff_end_pointer = 0;
  47.         }
  48.         else _uart_send_buff_end_pointer++;


  49.         if(start_flag){
  50.                 USART1_CR1_bit.TXEIE = 1;                // Enable Transmitter
  51.         }

  52.         return 1;
  53. }



  54. int UARTGetChar(void)
  55. {
  56.         int c;

  57.         if(_uart_recv_buff_start_pointer == _uart_recv_buff_end_pointer) return -1;

  58.         c = _uart_recv_buff[_uart_recv_buff_start_pointer];
  59.         if((myStdioBuffRecvSize-1) == _uart_recv_buff_start_pointer){
  60.                 _uart_recv_buff_start_pointer = 0;
  61.         }
  62.         else{
  63.                 _uart_recv_buff_start_pointer++;
  64.         }
  65.         return c;
  66. }



  67. void USART1_IRQHandler(void)
  68. {
  69.         if(USART1_ISR_bit.TXE)
  70.         {
  71.                 if(_uart_send_buff_start_pointer == _uart_send_buff_end_pointer)
  72.                 {
  73.                         USART1_CR1_bit.TXEIE = 0;                // Disable Transmitter
  74.                 }
  75.                 else
  76.                 {
  77.                         USART1_TDR = _uart_send_buff[_uart_send_buff_start_pointer];
  78.                         if((myStdioBuffSendSize-1) == _uart_send_buff_start_pointer){
  79.                                 _uart_send_buff_start_pointer = 0;
  80.                         }
  81.                         else{
  82.                                 _uart_send_buff_start_pointer++;
  83.                         }
  84.                 }
  85.         }
  86.        
  87.         if(USART1_ISR_bit.RXNE)
  88.         {
  89.                 /*
  90.                 if((_uart_recv_buff_start_pointer+1) == _uart_send_buff_end_pointer ||
  91.                    (_uart_send_buff_start_pointer-255 == _uart_send_buff_end_pointer)){
  92.                            // Receiver buff overflow
  93.                            USART1_RQR_bit.RXFRQ = 1;                // clear flag
  94.                    }
  95.                 else{
  96.                         _uart_recv_buff[_uart_send_buff_start_pointer] = USART1_RDR;
  97.                         if((myStdioBuffRecvSize-1) == _uart_recv_buff_end_pointer){
  98.                                 _uart_recv_buff_end_pointer = 0;
  99.                         }
  100.                         else{
  101.                                 _uart_recv_buff_end_pointer++;
  102.                         }
  103.                 }
  104.                 */
  105.                 _uart_recv_buff[_uart_recv_buff_end_pointer] = USART1_RDR;
  106.                 if((myStdioBuffRecvSize-1) == _uart_recv_buff_end_pointer){
  107.                         _uart_recv_buff_end_pointer = 0;
  108.                 }
  109.                 else{
  110.                         _uart_recv_buff_end_pointer++;
  111.                 }

  112.         }

  113.         return;
  114. }
複製代碼



發表於 2020-4-13 12:10:36 | 顯示全部樓層
老闆厲害. 有木有考慮開源後把完整的項目存到例如 GitHub 等平台
 樓主| 發表於 2020-4-16 00:17:01 | 顯示全部樓層
main.h:

  1. #ifndef MyMainInc
  2. #define MyMainInc

  3. #include <stdlib.h>
  4. #include <ST/iostm32f051x4.h>


  5. #define FPCLK                        12000000UL
  6. #define USART1_BAUD                115200UL
  7. #define USARTDIV                 (2*FPCLK/USART1_BAUD)                // 208.333


  8. #define WWDG_IRQ_NUMBER                        0
  9. #define Reseved1_IRQ_NUMBER                1
  10. #define RTC_WKUP_IRQ_NUMBER                2
  11. #define FLASH_IRQ_NUMBER                3
  12. #define RCC_IRQ_NUMBER                        4
  13. #define EXT01_IRQ_NUMBER                5
  14. #define EXT23_IRQ_NUMBER                6
  15. #define EXT45_IRQ_NUMBER                7
  16. #define Reseved8_IRQ_NUMBER                8
  17. #define DMA1_IRQ_NUMBER                        9
  18. #define DMA23_IRQ_NUMBER                10
  19. #define DMA45_IRQ_NUMBER                11
  20. #define ADC_IRQ_NUMBER                        12
  21. #define TIM1_BRK_IRQ_NUMBER                13
  22. #define TIM1_CC_IRQ_NUMBER                14
  23. #define Reseved15_IRQ_NUMBER        15
  24. #define TIM3_IRQ_NUMBER                        16
  25. #define TIM6_IRQ_NUMBER                        17
  26. #define Reseved18_IRQ_NUMBER        18
  27. #define TIM14_IRQ_NUMBER                19
  28. #define TIM15_IRQ_NUMBER                20
  29. #define TIM16_IRQ_NUMBER                21
  30. #define TIM17_IRQ_NUMBER                22
  31. #define I2C1_IRQ_NUMBER                        23
  32. #define I2C2_IRQ_NUMBER                        24
  33. #define SPI1_IRQ_NUMBER                        25
  34. #define SPI2_IRQ_NUMBER                        26
  35. #define USART1_IRQ_NUMBER                27
  36. #define USART2_IRQ_NUMBER                28
  37. #define USART345_IRQ_NUMBER                29
  38. #define Reseved30_IRQ_NUMBER        30
  39. #define USB_IRQ_NUMBER                        31

  40. void DIS_LED_polling(void);


  41. #if 0
  42. // for GCC
  43. int USARTWriteChar(char, FILE *);
  44. int USARTReadChar(FILE *);

  45. FILE uart_str = __FDEV_SETUP_STREAM(USARTWriteChar, USARTReadChar, _FDEV_SETUP_RW);
  46. stdin = stdout = &uart_str;

  47. int USARTWriteChar(char chr, FILE *fp)
  48. {
  49.         return 0;
  50. }


  51. int USARTReadChar(FILE *fp)
  52. {
  53.         return 0x20;
  54. }

  55. #endif



  56. #endif
複製代碼

display.c:
  1. #include "main.h"


  2. #define DIS_LED_NUM_0                0xc0ff        // 0b1100 0000
  3. #define DIS_LED_NUM_1                0xf9ff        // 0b1111 1001
  4. #define DIS_LED_NUM_2                0xa4ff        // 0b1010 0100
  5. #define DIS_LED_NUM_3                0xb0ff        // 0b1011 0000
  6. #define DIS_LED_NUM_4                0x99ff        // 0b1001 1001
  7. #define DIS_LED_NUM_5                0x92ff        // 0b1001 0010
  8. #define DIS_LED_NUM_6                0x82ff        // 0b1000 0010
  9. #define DIS_LED_NUM_7                0xd8ff        // 0b1101 1000
  10. #define DIS_LED_NUM_8                0x80ff        // 0b1000 0000
  11. #define DIS_LED_NUM_9                0x90ff        // 0b1001 0000
  12. #define DIS_LED_NUM_NULL        0xffff


  13. extern unsigned char DIS_LED_flag;
  14. extern unsigned char RMS_result_c[4];
  15. extern unsigned char LED_flash_flag;
  16. extern unsigned char DM_range;


  17. unsigned short DIS_LED_getstring(int c)
  18. {
  19.         switch(c){
  20.         case 0: return DIS_LED_NUM_0;
  21.         case 1: return DIS_LED_NUM_1;
  22.         case 2: return DIS_LED_NUM_2;
  23.         case 3: return DIS_LED_NUM_3;
  24.         case 4: return DIS_LED_NUM_4;
  25.         case 5: return DIS_LED_NUM_5;
  26.         case 6: return DIS_LED_NUM_6;
  27.         case 7: return DIS_LED_NUM_7;
  28.         case 8: return DIS_LED_NUM_8;
  29.         case 9: return DIS_LED_NUM_9;
  30.         default: return DIS_LED_NUM_NULL;
  31.         }
  32. }


  33. void DIS_LED_polling(void)
  34. {
  35.         static unsigned char cnt;

  36.         if(DIS_LED_flag)
  37.         {
  38.                 GPIOA_BSRR_bit.BR4 = 1;                        // reset RA4

  39.                 DIS_LED_flag = 0;
  40.                 GPIOA_BSRR_bit.BS1 = 1;                        // power common line at X1 digit
  41.                 GPIOA_BSRR_bit.BS2 = 1;                        // power common line at X10 digit
  42.                 GPIOA_BSRR_bit.BS3 = 1;                        // power common line at X100 digit
  43.                 GPIOA_BSRR_bit.BS6 = 1;                        // power common line at X1000 digit
  44.                
  45.                 switch(cnt){
  46.                 case 0:
  47.                         if(0 == DM_range){
  48.                                 if(LED_flash_flag){
  49.                                         SPI1_DR = DIS_LED_getstring(RMS_result_c[0]);
  50.                                 }
  51.                                 else{
  52.                                         SPI1_DR = DIS_LED_getstring(RMS_result_c[0]) & 0x7fff;        // dp
  53.                                 }
  54.                         }
  55.                         else{
  56.                                 SPI1_DR = DIS_LED_getstring(RMS_result_c[0]);
  57.                         }
  58. //                        GPIOA_BSRR_bit.BR1 = 1;                        // Output Low Level
  59.                         break;
  60.                 case 1:
  61.                         SPI1_DR = DIS_LED_getstring(RMS_result_c[1]);
  62. //                        GPIOA_BSRR_bit.BR2 = 1;                        // Output Low Level
  63.                         break;
  64.                 case 2:
  65.                         if(DM_range){
  66.                                 if(LED_flash_flag){
  67.                                         SPI1_DR = DIS_LED_getstring(RMS_result_c[2]);
  68.                                 }
  69.                                 else{
  70.                                         SPI1_DR = DIS_LED_getstring(RMS_result_c[2]) & 0x7fff; // dp
  71.                                 }
  72.                         }
  73.                         else{
  74.                                 SPI1_DR = DIS_LED_getstring(RMS_result_c[2]);
  75.                         }
  76. //                        GPIOA_BSRR_bit.BR3 = 1;                        // Output Low Level
  77.                         break;
  78.                 default:
  79.                         SPI1_DR = DIS_LED_getstring(RMS_result_c[3]);
  80. //                        GPIOA_BSRR_bit.BR6 = 1;                        // Output Low Level
  81.                         break;
  82.                 }
  83.                 if(3 == cnt) cnt=0;
  84.                 else cnt++;
  85.         }
  86.         else{
  87.                 if(SPI1_SR_bit.TXE){
  88.                         GPIOA_BSRR_bit.BS4 = 1;                                // a clock pulse on RA4 will to execute that DAT moving from shift register to store register
  89.                                                                                                 // generating a PCLK for HC595
  90.                         switch(cnt){
  91.                         case 1:
  92.                                 GPIOA_BSRR_bit.BR1 = 1;                        // Output Low Level
  93.                                 break;
  94.                         case 2:
  95.                                 GPIOA_BSRR_bit.BR2 = 1;                        // Output Low Level
  96.                                 break;
  97.                         case 3:
  98.                                 GPIOA_BSRR_bit.BR3 = 1;                        // Output Low Level
  99.                                 break;
  100.                         default:
  101.                                 GPIOA_BSRR_bit.BR6 = 1;                        // Output Low Level
  102.                                 break;
  103.                         }
  104.                 }
  105.         }

  106. //        GPIOA_BSRR_bit.BS2 = 1;
  107. //        GPIOA_BSRR_bit.BS3 = 1;
  108. //        GPIOA_BSRR_bit.BS6 = 1;

  109. }
複製代碼

今天進行整機測試,發現與電錶的結果有一段不算小的差距。我還以為演算法出了什麼問題?

最後經過測試確認為 STM32 本身 ADC 的增益誤差所導至的結果 (昏倒了!)……所以我再加了一段修正碼進去。為了方便我就直接寫了 magic number 在裡面了。我解釋一下推算過程:ADC 結果為 115 時與實際值相同;ADC 結果為 2555 時實際值為 2476。以此為標準所推算出的數據就是「1984」與「115」,用於 MainLoop_ADC_caluation_polling() 函式裡。
final_value = (ADC_value - 115) * 0.96785 + 115;
寫成整數的算法為
final_value = (ADC_value - 115) * 1984 / 2048 + 115;

再來將直流直接灌入 PA0 得到的結果是 OK 的,但改成灌交流 (經過一堆零件) 進入 PA0 後結果出現了 offset error 的問題,而且正負半週的結果還不一樣。只能先將問題歸究在雜訊。示波器顯示,前端來的信號有很大的雜訊……為什麼 AMC1200 這顆會產生這麼大的雜訊?

再來還是雜訊的問題。無信號輸入時會有底噪讀數出現。問題源頭的一半同在 AMC1200 身上。

再來就是 MCU 本身所產生的巨大雜訊,由 PA0 就可以明顯的看到,從內部反衝回來的突波……

也就是說,所有 ADC 會碰到的問題,這次我全都遇上了……

使用 STM32 內建的 ADC 做 1mV 信號的高速解析工作,這點恐怕是行不通。若是改為 10mV 的話就會好很多,如此的話最多只會有最後的一兩位數在跳……現在的情況是差了 10 至 20 位。

我想,不經過這些雜七雜八的直接把交流信號直接灌到 PA0 的話應該就沒這些問題了。這個系統的成敗關鍵點會在類比前端身上。


評分

4

查看全部評分

 樓主| 發表於 2020-4-17 00:43:23 | 顯示全部樓層
恭喜發財呀!

main.c.zip (4.15 KB, 下載次數: 7, 售價: 9 個酷幣)

這是最關鍵的一個檔案。精華都集中在裡面了。

本檔案保留一年喔!明天的今天拿到這檔案的人就可以直接公開裡面的內容了。在這之前呢,就讓我賺一點外快吧!

裡面的求平方根倒數,源自在網路上流傳 DOOM 的程式碼,被修改多次後得到了最精華版本,我直接 copy 過來用了。其它 的部分是自己寫的,整理後重新發佈,或做成模組來賺錢都可以。

裡面有不少的測試碼留著沒有刪除,請自行慢慢研究。




評分

1

查看全部評分

 樓主| 發表於 2020-4-17 00:59:42 | 顯示全部樓層
雜訊問題頭痛了好幾天,終於得到了突破性發展。

3.jpg

這是我試作的,精密全波整流器。重點在兩個地方,第一,OPA 要選好一點的;第二,輸出到 MCU 之前一定要過隨偶器。之後再測試下來的結果是,除了空信號時還有四到六位的底噪外,基本性能已經與市售電錶差不多了。如果能再做一版,應該就可以解決這問題。

那很遺憾的是,因為使用到了六組低漂移低雜訊 OPA,這三顆 IC 的要價呀,基本已經可以買上一顆 RMS 計算器了……

各位有時間的可以玩玩看努力一下,有沒有什麼方法可以填平這錢坑的?不然滿街便宜的 RMS 錶頭,這目標恐怕是達不到了。


發表於 2020-4-17 10:50:16 | 顯示全部樓層
用迴歸分析法? (以前聽開發說的)
發表於 2020-4-17 11:26:43 | 顯示全部樓層
買了附件,不過初心者單純喜歡買這離我太遠

評分

1

查看全部評分

發表於 2020-4-17 11:46:52 | 顯示全部樓層
試試看直接kalman filter 或者做 DFT 再算
發表於 2020-4-17 17:13:19 | 顯示全部樓層
很多 rms 是直接由 mcu 直接取樣計算,不用經過 opa~
stm32 官方也有一個範例不用 opa的

Watt-hour meter based on the STM32F101 microcontroller
https://www.st.com/resource/en/a ... icroelectronics.pdf

 樓主| 發表於 2020-4-17 19:40:44 | 顯示全部樓層
tsai5371 發表於 2020-4-17 11:26 AM
買了附件,不過初心者單純喜歡買這離我太遠

感謝支持!這已經是屬於 EE/IT 深專業綜合類別的東西,會用得到的人應該是不多。

凡事無所求,只求無憂無惱無災難。花時間做這東西我更喜歡的是泡杯茶吃餅乾然後拿本佛經看上一整天。
 樓主| 發表於 2020-4-18 00:31:04 | 顯示全部樓層
jojoling 發表於 2020-4-17 05:13 PM
很多 rms 是直接由 mcu 直接取樣計算,不用經過 opa~
stm32 官方也有一個範例不用 opa的

如果說把規格改低一點,比方解析度調為 100mV,那普通的整流橋加幾顆分壓電阻應該就足以勝任。只是說,若現成產品是這種模樣,我是完全不考慮採用的。我想其他人大概也會得出一樣的結論才是……
發表於 2020-4-18 19:50:41 | 顯示全部樓層
gsm7 發表於 2020-4-18 12:31 AM
如果說把規格改低一點,比方解析度調為 100mV,那普通的整流橋加幾顆分壓電阻應該就足以勝任。只是說,若 ...

我一時找不到那個直接取樣的網頁。
其實不用 opa 並不會真的變差。

它的基礎原理就是將正負的交流信號在取樣前面加上 1/Vdd (5V adc 的話取 2.5V) ,這個做法是在很多的電表在量測交流時也會使用的設計,同時也可以固定輸入阻抗。就不用加上 opa 當上buffer。
有的設計是利用差動的opa放大,確保可以完整的取樣到信號而不會被地端的干擾信號影響。

在一般便宜又精密的表頭裏,一般會使用 OP07系列,再另外處理正負雙電源,成本會低非常多。

在單純mcu設計來說,另一點並不表示它精度差,在非常多的商用 mcu 裏有滿多的設計就是簡化外部元件的。如內部 adc 使用雙點差動輸入取樣,內部 adc內有 gain 可調整,內部 Vref 有多組選擇,這些都是提升精度用。
 樓主| 發表於 2020-4-19 10:13:22 | 顯示全部樓層
jojoling 發表於 2020-4-18 07:50 PM
我一時找不到那個直接取樣的網頁。
其實不用 opa 並不會真的變差。

meter IC 就是直接輸入,無外掛 OPA,差動輸入,有些還含 MCU。所以到底是 MCU 包 meter 還是 meter 包 MCU 這就頗耐人尋味了。

而硬體方案一開始的時候就已經決定好了。

這次試作過程中得到了不少收獲,問題點已經收集的差不多了,功力也略有增進了一些。雖然這部分沒有詳細交待 (提個一兩句意思一下),因為開這樓的目的並不在這邊。

各位有興趣的人可以實作看看,參予實作才能做中學。除了做別人提供現成的套件外,我還沒碰過能一路順到底中間不出過問題的項目呢。
 樓主| 發表於 2020-4-19 10:15:10 | 顯示全部樓層
xiaolaba 發表於 2020-4-17 11:46 AM
試試看直接kalman filter 或者做 DFT 再算

嗯……這個似乎離我太遠了,離 Cortex-M0 可能也有一段不小的距離。如果玩驗證的話,因為程式預設每秒有 20,000 bytes 的資料量產生,UART 的速度需要再提高四倍,送到 PC 直接給 MATLAB 這類的去處理就好。直接畫成圖顯示出來就成虛擬示波器了。

不在 PC 上 RUN 的話,可能得考慮 DSP 才行……之後的軟體又是另外一回事。
發表於 2020-4-20 09:09:40 | 顯示全部樓層
gsm7 發表於 2020-4-19 10:15 AM
嗯……這個似乎離我太遠了,離 Cortex-M0 可能也有一段不小的距離。如果玩驗證的話,因為程式預設每秒有  ...

你的ADC 是開了DMA還是自己填ARRAY
開DMA就足夠DSP出力, 100KHz 應該不成問題, 直接 AM 解調做 SDR 都可以到 900KHz
自己填ARRAY的話, 也可以試試看每個ADC值先做最簡單的濾波再存ARRAY, 以下是類似硬體的 RC 濾波器, 運算速度沒有負擔, 32BIT的機器更不是問題.


  1. //DSP RC filter
  2. #define delay_constant 6   // factor for RC constant, adjustment for damping ratio, greater the longer response time

  3. singed int_32 temp;           // RC holder – 32 bits
  4. singed int_16 input;           // ADC input – 16 bits
  5. singed int_16 output;         // RC output – 16 bits

  6. // add current sample to RC holder
  7. temp = input + temp - (temp >> delay_constant) ;

  8. // output, filtered
  9. output = temp >> delay_constant;
複製代碼
 樓主| 發表於 2020-4-21 21:53:15 | 顯示全部樓層
本帖最後由 gsm7 於 2020-4-21 10:01 PM 編輯
xiaolaba 發表於 2020-4-20 09:09 AM
你的ADC 是開了DMA還是自己填ARRAY
開DMA就足夠DSP出力, 100KHz 應該不成問題, 直接 AM 解調做 SDR 都可 ...


單 CPU 已經夠力,使用 DMA 會多了一些工作,玩票質性的東西以自己方便為主。

以前弄過幾次壓榨硬體運算力的東西,等它出來,相關產品早就已經爛大街了。現在速食業盛行,做精品的只剩牆角能站,再過一陣子搞不好就全部滅亡了。

現在沒有做產品,所以沒有必要自己去搞這些東西。能有現成的就盡量用現成的,call 指令一行就能搞定的事,何苦要為難自己去跟頂端的那些怪物們爭呢?最多 MATLAB 還是 LABVIEW 叫出來玩一玩就好,在 MCU 上面跑高等定點 DSP 諸如此類的,我應該是不會再去接觸這一塊及以上的東西。

幹維修,生活多輕鬆!還能為地球環保出一點微薄之力……
發表於 2020-4-23 20:06:58 | 顯示全部樓層
學習了.感謝
發表於 2020-5-1 23:01:46 | 顯示全部樓層
請問boot0直接接到接地時以SWD讀寫MCU的FLASH檔案(.bin)會有問題嗎?
您需要登錄後才可以回帖 登錄 | 立即註冊

本版積分規則

關閉

站長小叮嚀上一條 /1 下一條

禁閉室|手機版|連繫我們|痞酷網電子技術論壇

GMT+8, 2024-3-28 10:07 PM , Processed in 0.140586 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.