Jasson's Simple Life https://jyouknownit.com My Coding & Life - 記錄編程與生活的網站 Sun, 14 Apr 2024 04:44:55 +0000 zh-TW hourly 1 https://wordpress.org/?v=6.4.4 https://i0.wp.com/jyouknownit.com/wp-content/uploads/2023/10/cropped-Zeus1.jpg?fit=32%2C32&ssl=1 Jasson's Simple Life https://jyouknownit.com 32 32 183344200 C# WPF DrawingContext 高效繪圖 – 波形圖 (Drawing Waveform Chart) https://jyouknownit.com/csharp-wpf-drawingcontext-efficient-drawing-waveform-chart/?utm_source=rss&utm_medium=rss&utm_campaign=csharp-wpf-drawingcontext-efficient-drawing-waveform-chart https://jyouknownit.com/csharp-wpf-drawingcontext-efficient-drawing-waveform-chart/#respond Sun, 14 Apr 2024 07:15:00 +0000 https://jyouknownit.com/?p=2021 在WPF DrawingContext 是一個核心元素,提供了一個環境用於進行底層繪圖操作。透過使用 DrawingContext,你可以在WPF應用程式中繪製圖形、文字、圖像等。它是一個向量繪圖的介面,意味著你繪製的內容可以無損縮放,非常適合構建高分辨率的UI介面。

C# WPF DrawingContext 高效繪圖 – 波形圖 (Drawing Waveform Chart)〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
在WPF DrawingContext 是一個核心元素,提供了一個環境用於進行底層繪圖操作。透過使用 DrawingContext,你可以在WPF應用程式中繪製圖形、文字、圖像等。它是一個向量繪圖的介面,意味著你繪製的內容可以無損縮放,非常適合構建高分辨率的UI介面。

DrawingContext 並不直接用於XAML文件中,而是通常在代碼後台中使用,尤其是在自定義控件的渲染過程中。你可以通過覆蓋控件的 OnRender 方法並使用傳遞給該方法的 DrawingContext 參數來繪製自定義的UI元素。

下面是 DrawingContext 提供的一些繪製功能:

  • 繪製形狀:如線條、矩形、橢圓、多邊形和路徑。這些形狀可以被填充或描邊,也可以同時被填充並描邊。
  • 繪製文字:可以指定文字的字體、大小、顏色等屬性,並在UI上繪製。
  • 繪製圖像:允許你將圖像資源繪製到UI上。
  • 應用變換:可以對繪製的內容應用各種變換操作,如平移、旋轉、縮放等。
  • 剪裁和不透明遮罩:可以通過剪裁來限制繪製內容的區域,或使用不透明遮罩來控制內容的可見度。

使用 DrawingContext 進行繪製時,所有的繪製命令都會被緩存,直到當前的繪製作用域結束。這意味著,DrawingContext 的操作不是立即執行的,而是在作用域結束時一次性處理,這有助於提高繪製性能。

由於 DrawingContext 是用於更底層的繪製操作,因此它提供了強大的靈活性,但同時也意味著比在XAML中聲明式繪圖要複雜一些。如果你打算自定義WPF控件的外觀或創建複雜的圖形界面,那麼了解和使用 DrawingContext 將非常有幫助。

Outline

DrawingContext 繪圖基礎

範例展示了如何在WPF的自定義控件中覆蓋OnRender方法,使用DrawingContext來繪製線條、矩形、橢圓。我們將會創建一個簡單的自定義控件,並在其中進行這些基本圖形的繪製。

首先,確保您的項目引用了WPF的命名空間:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

DrawLine Example

// 繪製線條
Pen linePen = new Pen(Brushes.Black, 2);
drawingContext.DrawLine(linePen, new Point(10, 10), new Point(100, 10));
DrawingContext Darw Line Image
繪製線條

DrawRectagle Example

// 繪製矩形
drawingContext.DrawRectangle(Brushes.LightBlue, null, new Rect(10, 20, 90, 60));
DrawingContext Darw Rectagle Image
繪製矩形

DrawEllipse Example

// 繪製橢圓
// 使用DrawEllipse方法繪製橢圓,指定填充顏色(淺綠色)、無邊框(null)、中心點坐標和橢圓的半徑(X軸和Y軸)
drawingContext.DrawEllipse(Brushes.LightGreen, null, new Point(55, 120), 45, 30);
DrawingContext Draw Ellipse Image
繪製橢圓

DrawText Example

// 繪製文字
FormattedText formattedText = new FormattedText(
"Hello, WPF!", // 文字內容
System.Globalization.CultureInfo.CurrentCulture, // 使用當前文化信息
FlowDirection.LeftToRight, // 文字流向
new Typeface("Verdana"), // 字體
16, // 字號
Brushes.Black, // 文字顏色
VisualTreeHelper.GetDpi(this).PixelsPerDip); //Render在不同DPI的顯示器上能夠自動調整

var formattedTextPoint = centerPoint;
formattedTextPoint.Offset(- formattedText.WidthIncludingTrailingWhitespace / 2, - formattedText.Height / 2);
drawingContext.DrawText(formattedText, formattedTextPoint); // 指定文字的繪製位置 位於元件正中心

Sample Code: MyGithub

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace DrawingSamepleCode
{
    internal class CustomDrawingControl : Control
    {
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            var centerPoint = new Point(this.ActualWidth / 2, this.ActualHeight / 2);

            // 繪製線條
            Pen linePen = new Pen(Brushes.Black, 2);// 創建一個筆刷,定義線條的顏色(黑色)和寬度(2像素)
            // 使用DrawLine方法繪製線條,指定筆刷、起點和終點的坐標
            drawingContext.DrawLine(linePen, new Point(10, 10), new Point(100, 10));

            // 繪製矩形
            // 使用DrawRectangle方法繪製矩形,指定填充顏色(淺藍色)、無邊框(null)和矩形的位置及大小
            drawingContext.DrawRectangle(Brushes.LightBlue, null, new Rect(10, 20, 90, 60));

            // 繪製橢圓
            // 使用DrawEllipse方法繪製橢圓,指定填充顏色(淺綠色)、無邊框(null)、中心點坐標和橢圓的半徑(X軸和Y軸)
            drawingContext.DrawEllipse(Brushes.LightGreen, null, new Point(55, 120), 45, 30);


            // 繪製文字
            FormattedText formattedText = new FormattedText(
                "Hello, WPF!", // 文字內容
                System.Globalization.CultureInfo.CurrentCulture, // 使用當前文化信息
                FlowDirection.LeftToRight, // 文字流向
                new Typeface("Verdana"), // 字體
                16, // 字號
                Brushes.Black, // 文字顏色
                VisualTreeHelper.GetDpi(this).PixelsPerDip); //Render在不同DPI的顯示器上能夠自動調整

            var formattedTextPoint = centerPoint;
            formattedTextPoint.Offset(- formattedText.WidthIncludingTrailingWhitespace / 2, - formattedText.Height / 2);

            drawingContext.DrawText(formattedText, formattedTextPoint); // 指定文字的繪製位置 位於元件正中心
        }
    }
}

DrawingContext 繪製波形圖

在顯示大量波形的情況下,標準元件可能無法負擔(會導致延遲和功能受限)。因此,我提供了一個免費的範例,說明如何使用C# WPF高效繪製大量的波形圖。

Waveform 基礎介紹

要理解和分析電壓波形圖,你需要具備一些基本的電學知識和技能。如下:

  1. 電壓基礎
    • 定義:了解電壓是表示電位差的物理量,即兩點間電位差的度量。
    • 單位:電壓的單位是伏特(V)。
  2. 時間和頻率
    • 時間域:了解波形如何隨時間變化,這對於分析波形如何反映電壓隨時間的變動非常重要。
    • 頻率域:理解波形中的不同頻率成分,包括基本頻率和諧波。
  3. 正弦波和非正弦波
    • 正弦波:最常見的交流(AC)電壓波形,具有周期性和振幅一致的特點。
    • 非正弦波:包括方波、三角波等,常見於數位電路和某些類型的電源供應。
  4. 波形參數
    • 振幅:波形的最高點和最低點的絕對值。
    • 週期和頻率:週期是波形重複一次所需的時間,頻率是單位時間內波形重複的次數。
    • 相位:描述波形相對於參考點的偏移。

以上是繪製波形圖的基礎,理解之後能夠開始定義使用者能夠設置的參數。如下:

TimeResolution、Max/Min Voltage Per Pin、Cycle Count、Point Size Per Cycle

為了增強靈活性和通用性,我們可以開發一個功能,讓使用者在不同的測試周期(Cycle)中設定不同的點大小(PointSize)。例如,你可以配置兩個不同的周期,每個周期的持續時間和點大小都不同。具體設置如下:

  • 第一個周期(Cycle[0])的點大小設為10,對應的時間分辨率為1納秒(nS),因此這個周期的總持續時間為10納秒(nS)。
  • 第二個周期(Cycle[1])的點大小設為20,同樣的時間分辨率為1納秒(nS),使得這個周期的持續時間為20納秒(nS)。

這樣的設定允許每個周期內呈現不同的波形特性,並且這些設定可以根據實際需求自由調整。此外,若需要對所有周期使用相同的設定,也可以很容易地實現,增加了配置的一致性和可重複性。

DrawingContext Waveform 功能介紹

在我開發的元件中,我實現了多個高度客製化和靈活的功能,旨在提供卓越的使用體驗和效能:

  1. 多樣化的波形顯示:元件支持在每個測試周期內顯示每一個引腳(Pin)的波形,用戶可以自由定義每個周期的持續時間,確保了顯示的精確性和彈性。
  2. 擴展性和效能:理論上,元件支持無限數量的引腳和周期,以及每個引腳的波形數量。重要的是,這一設計不會影響元件的運行效率,因為系統僅繪製當前畫面中的波形,從而大幅降低了計算負擔。
  3. 高度客製化的界面:除了核心功能,用戶還可以自訂波形、光標、文本等元素的色彩,使得每個用戶都能根據自己的需求或品牌風格進行調整。

這些功能不僅提高了元件的實用性和靈活性,也保證了在處理複雜資料時的高效性能。

drawing waveform demo gif

Source Code: My Github

以上是我學習筆記提供給各位,還有很多功能能夠增加,如需要或技術上有任何問題可以跟我聯絡

C# WPF DrawingContext 高效繪圖 – 波形圖 (Drawing Waveform Chart)〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/csharp-wpf-drawingcontext-efficient-drawing-waveform-chart/feed/ 0 2021
STM32 USART UART #1 基礎應用 – USART基本概念 https://jyouknownit.com/stm32-usart-uart-1-basic-application-basic-concepts-of-usart/?utm_source=rss&utm_medium=rss&utm_campaign=stm32-usart-uart-1-basic-application-basic-concepts-of-usart https://jyouknownit.com/stm32-usart-uart-1-basic-application-basic-concepts-of-usart/#respond Sat, 27 Jan 2024 09:50:32 +0000 https://jyouknownit.com/?p=1987 STM32的USART和UART的基本概念。文章主要集中在異步串行通信的原理上,講解了數據幀的結構、波特率等關鍵參數。我還介紹了UART的硬件組成部分,包括如何通過寄存器來控制數據的傳輸和接收。此外,文章還涉及了USART中斷字符的傳輸和錯誤處理的重要性。

STM32 USART UART #1 基礎應用 – USART基本概念〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
STM32的USART和UART的基本概念。文章主要集中在異步串行通信的原理上,講解了數據幀的結構、波特率等關鍵參數。我還介紹了UART的硬件組成部分,包括如何通過寄存器來控制數據的傳輸和接收。此外,文章還涉及了USART中斷字符的傳輸和錯誤處理的重要性。

Outline

USART基本概念和常見的應用

USART(universal synchronous asynchronous receiver transmitter, 通用異步接收發送器)是一種在計算機硬件中常見的硬件通信協議,用於串行通信。它允許設備之間進行點對點的低速度數據交換。USART的主要特點和工作原理如下:

  1. 異步串行通信:異步意味著在傳輸數據時,發送方和接收方的時鐘不同步。UART通信不需要時鐘信號來同步發送和接收的數據,這是它與同步串行通信(如SPI或I2C)的主要區別。
  2. 數據格式:UART通信中的數據通常包括起始位、數據位(通常是7或8位)、可選的奇偶校驗位,以及停止位。這種格式確保接收器可以正確地識別和解析接收到的數據。
  3. 波特率:UART通信中的一個關鍵參數是波特率,即每秒傳輸的位數。發送方和接收方必須設置為相同的波特率以實現正確的通信。
  4. 硬件結構:一個典型的UART包括一個發送器(用於發送數據, transmitter)和一個接收器(用於接收數據, receiver),以及一個串行數據線(對於全雙工通信,分別有一條用於發送和一條用於接收)。
  5. 應用場景:UART廣泛應用於微控制器、計算機、通信設備等場景,用於低速度的點對點通信。它非常適合於數據量不大,對實時性要求不高的應用。
  6. 無需外部時鐘:由於UART是異步的,它不需要外部的時鐘信號。這使得UART設計相對簡單,並減少了硬件需求。

同步異步接收發送器(USART)有多種運用,涵蓋了不同的通信需求和協議。通常有哪些運用如下:

  1. 全雙工數據交換:USART能夠與外部設備進行全雙工數據交換,即同時進行數據的發送和接收。這對於需要高效通信的應用非常重要。
  2. 支持NRZ異步串行數據格式:NRZ(Non-Return to Zero)格式廣泛用於工業標準的串行通信,USART的這一特性使其兼容多種設備。
  3. 廣泛的波特率範圍:通過使用分數波特率生成器,USART能夠支持非常廣泛的波特率,使其適用於多種不同速率的通信需求。
  4. 同步和半雙工通信:USART支持同步一向通信和半雙工單線通信,這增加了其在不同通信模式下的靈活性。
  5. 支持多種通信協議和標準
    • LIN(Local Interconnect Network):一種用於車載網絡的串行網絡協議。
    • 智能卡協議(smart card):用於智能卡和讀卡器之間的通信。
    • IrDA(Infrared Data Association)SIR ENDEC:一種紅外數據通信標準。
    • 調制解調器操作(例如CTS/RTS):允許USART在調制解調器通信中使用。
  6. 多處理器通信:USART支持多處理器之間的通信(address mask),這對於複雜的系統和應用非常重要。
  7. 高速數據通信:通過使用DMA(直接內存訪問)進行多緩衝配置,USART可以實現高速數據傳輸,這對於要求高速數據處理的應用非常關鍵。

USART是一種多功能的通信工具,適用於多種不同的應用場景,從工業控制到車載系統,再到智能卡和無線通信,USART的多樣性和靈活性使其成為串行通信中一個強大的解決方案。

USART基本運作原理

做為串列通訊的工具,需要知道基本通訊的格式,如下:

USART/UART Data frame.
USART/UART Data frame.

在上方圖中顯示了幾種基本的data frame,而在一個frame中是由起始位(start bit)、停止位(stop bit)、可選的奇偶校驗位(parity bit,optional),以及數據位(7或8位)所組合成。其中注意兩個部分,Idle freme表示當前是閒置狀態,Break Frame是指數據線被有意或無意地保持在低電位一段時間(超過一個frame的時間),通常用於指示特殊情況或進行故障排除。

接下來介紹USART在STM32中是如何運作的,其中使用了那些Register,如下:

在下方圖USART block diagram中,首先注意背景灰色框框的部分,分成兩組分別為Transmit和Receive,再細分能夠分成Transmit Data Register (TDR)、Transmit Shift RegisterReceive Data Register (RDR)、Receive Shift Register兩大部分。談談如何接收資料,首先外部資料透過Rx傳輸進來,再透過Receive Shift Register接收傳至RDR,最後由CPU或DMA的方式將資料放置SRAM內部,讓User能夠取其資料使用。反之,如果要將資料傳輸出去,能夠透過CPU或DMA方式將SRAM資料帶入到TDR內,最後再透過Transmit Shift Register傳輸至Tx,以上是基本的USART傳輸方式。

USART block diagram.
USART原理圖。
USART原理圖

以上內容概述了USART的整體運作機制。接下來,我們將詳細介紹如何通過寄存器來控制數據傳輸和接收,探討各種中斷功能,以及如何確保資料傳輸的準確性。

USART – 單一字元傳輸與接收 (Singal Byte Communication)

字符傳輸

  1. TXE位的清除機制
    • TXE位在寫入數據寄存器時總是被清除。這意味著當有新數據被寫入數據寄存器時,TXE位會重置。
  2. TXE位設置的含義
    • 硬件設置TXE位,表示以下幾點:
      • 數據已從TDR(Transmit Data Register,傳輸數據寄存器)移動到移位寄存器,並且數據傳輸已經開始。
      • TDR寄存器是空的。
      • 可以在不覆蓋前一數據的情況下,將下一數據寫入USART_DR(USART Data Register,USART數據寄存器)。
  3. 中斷產生條件
    • 如果TXEIE(Transmit Data Register Empty Interrupt Enable)位被設置,TXE標誌將生成一個中斷。這意味著當TDR準備好接收新的數據時,系統可以配置為自動發出一個中斷信號。
  4. 數據寫入過程
    • 當正在進行數據傳輸時,對USART_DR(USART數據寄存器)的寫入操作會將數據存儲在TDR(傳輸數據寄存器)中。當前傳輸完成後,這些數據會被複製到移位寄存器中。
    • 當沒有進行數據傳輸時,對USART_DR的寫入操作會直接將數據放入移位寄存器,隨即開始數據傳輸,並且TXE(傳輸數據寄存器空)位立即被設置。
  5. TXE位和TC位的行為
    • 如果一個數據幀(包括停止位)被傳輸並且TXE位被設置,則TC(傳輸完成)位會變高。
    • 如果在USART_CR1(控制寄存器1)中設置了TCIE(傳輸完成中斷啟用)位,當TC位變高時會生成一個中斷。
  6. 關閉USART或進入低功耗模式前的要求
    • 在向USART_DR寫入最後一筆數據後,必須等待TC=1(表示傳輸完成)才能禁用USART或使微控制器進入低功耗模式。
Single Byte Communication Flow Chart
Single Byte Communication Flow Chart

流程圖說明:

  1. 將資料’A’進行傳輸,寫入至TDR,此時TXE(Transmit Data Register Empty)會被硬體重置。
  2. 資料會經由TDR隨著Clock帶入至Transmit Shift Register,這時TXE會被硬體設置,同時如果TXEIE有被啟用,則會發生中斷。
  3. 當資料透過Transmit Shift Register傳輸出去且完成,這時TC(Transmission Complete)會被硬體設置,如果有設置TCIE則會產生中斷(可軟體重置)。

請注意,上述流程適用於單字節傳輸。對於多字節傳輸情況,要使TC(傳輸完成)位被設置,除了完成當前傳輸外,還必須滿足TDR(傳輸數據寄存器)為空,即TXE(傳輸數據寄存器空)位被設置的條件。只有在這些條件都滿足時,TC位才會被設置,從而觸發TC中斷。

USART - TX傳輸時序圖
USART – TX傳輸時序圖

字符接收

  1. 字符接收過程
    • 在USART接收過程中,數據通過RX引腳按最低有效位先進行移位。在此模式下,USART_DR寄存器包含一個緩衝區(RDR),位於內部總線和接收移位寄存器之間。
  2. 接收設置步驟
    • 啟用USART:通過將USART_CR1寄存器中的UE位設置為1來啟用USART。
    • 設定字長:在USART_CR1中設定M位,以定義字長。
    • 設定停止位:在USART_CR2中設定停止位的數量。
    • 選擇DMA使能:如果進行多緩衝通信,在USART_CR3中選擇DMA使能(DMAR),並按多緩衝通信中解釋的方式配置DMA寄存器。
    • 選擇波特率:使用波特率寄存器USART_BRR來選擇所需的波特率。
    • 設置接收使能:設置USART_CR1中的RE位。這啟用接收器,開始尋找起始位。
  3. 字符接收時的行為
    • 當字符被接收時,RXNE(接收數據寄存器非空)位被設置,表示移位寄存器的內容已轉移到RDR。換句話說,數據已被接收,可以讀取(以及相關的錯誤標誌)。
    • 如果設置了RXNEIE位,則會生成中斷。
    • 如果在接收過程中檢測到帧錯誤、噪聲或溢出錯誤,則可以設置錯誤標誌。
    • 在多緩衝模式下,每接收一個字節後RXNE被設置,並通過DMA讀取數據寄存器來清除。
    • 在單緩衝模式下,通過軟件讀取USART_DR寄存器來清除RXNE位。RXNE標誌也可以通過寫入零來清除。在接收下一個字符結束前必須清除RXNE位,以避免溢出錯誤。
  4. 注意事項
    • 在接收數據時不應重置RE位。如果在接收過程中禁用了RE位,則當前字節的接收將被中斷。

以上是USART傳輸接收流程筆記。

USART – Break Characters

在上小節了解Break frame用意之後,接下來說明如何傳送和接收Break frame:

USART – Transmit Break frame

通信中如何使用SBK(Send Break)位來傳送中斷字符(break character),以及相關的操作細節。以下是對這段文字的分析:

  1. 發送中斷字符
    • 通過設置SBK位,USART會傳送一個中斷字符。中斷幀的長度取決於M位的設定(M位用於定義數據位的長度)。
  2. SBK位的操作
    • 當SBK位設置為‘1’時,在完成當前字符傳輸後,USART會在TX(傳輸線)上發送一個中斷字符。
    • 中斷字符傳輸完成時(即在中斷字符的停止位期間),該位會由硬件自動重置。
  3. 確保下一幀的識別
    • USART在最後一個中斷幀的末尾插入一個邏輯1位,以保證下一數據幀的起始位能被正確識別。
  4. 操作注意事項
    • 如果在中斷傳輸開始前,軟件重置了SBK位,則中斷字符將不會被傳輸。
    • 對於連續兩次的中斷,應在前一次中斷的停止位之後再設置SBK位。

USART – Receive Break frame

  1. 中斷字符的接收
    • 在USART通信中,當接收端檢測到一個中斷字符時,這通常表示數據線被有意或無意地保持在低電平一段時間,這段時間超過了傳輸一個正常數據位的時間。
  2. 處理為幀錯誤
    • USART將接收到的中斷字符視為一種幀錯誤(framing error)。幀錯誤是串行通信中的一種錯誤類型,指的是數據幀的格式不符合預期,例如缺失停止位。
    • 在這種情況下,USART偵測到的不是正常的數據幀結構(如起始位、數據位、可選的奇偶校驗位和停止位),而是一個異常的低電平信號,因此將其解釋為幀錯誤。
  3. 錯誤處理
    • 當USART檢測到幀錯誤時,它可能會通過設置錯誤標誌位、生成錯誤中斷或採取其他錯誤處理措施來響應這種情況。
    • 接收到的中斷字符通常用於通信協議中的特殊目的,如指示通信的開始或結束,或用於錯誤恢復和診斷。

USART – Receiver Start bit detection

在USART中,當識別出特定的採樣序列時,起始位被檢測到。這個序列是:1 1 1 0 X 0 X 0 X 0 0 0 0。

偵測start bit時序圖
偵測start bit時序圖
  1. 起始位檢測的中止
    • 如果起始位的檢測序列不完整,起始位檢測將被中止,接收器返回到閒置狀態,不設置任何標誌位,在這個狀態下,它將等待下一個下降沿(表示新的起始位的可能開始)。
  2. 起始位的確認
    • 起始位被確認(RXNE接收數據寄存器非空標誌位設置,如果RXNEIE=1則生成中斷),如果在兩次採樣中,3個被採樣的位都為0(第一次採樣在第3、第5和第7位,第二次採樣在第8、第9和第10位)。
  3. 起始位的驗證與噪聲標誌
    • 起始位被驗證(RXNE標誌位設置,如果RXNEIE=1則生成中斷),但如果在兩次採樣中,至少有2個採樣位是0,則設置NE(噪聲)標誌位。如果這個條件不滿足,則起始位檢測被中止,並返回閒置狀態(不設置任何標誌位)。
    • 如果在其中一次採樣(第3、第5和第7位或第8、第9和第10位的採樣)中,發現2個位為0,則起始位被驗證,但設置NE噪聲標誌位。

::在之後的文章會特別說明如何用軟體實現start bit detection。

UART與USART的比較

下方是UART和USART比較表格:

特性UART(通用異步接收發送器)USART(通用同步異步接收發送器)
通信方式只支持異步通信支持同步和異步通信
時鐘同步不需要外部時鐘信號同步模式下需要外部時鐘信號
數據傳輸率相對較低,適合低速通信在同步模式下可以達到較高的數據傳輸率
技術複雜性較低,硬件設計簡單較高,特別是在同步模式下
應用場景低速、低成本、低功耗應用,如簡單嵌入式系統高速數據傳輸和複雜通信要求的場景,如高性能嵌入式系統
主要優勢簡單性和成本效益速度和靈活性
UART和USART比較表格

UART和USART最主要的區別在於它們的通信方式。UART專門用於異步通信,而USART則支持同步和異步通信。

  1. 通信方式
    • UART:只支持異步通信,即數據傳輸時沒有時鐘信號同步。這使得UART在設計上更為簡單,但通信速度通常較慢。
    • USART:可以進行同步或異步通信。同步通信通常比異步通信快,但它需要額外的時鐘線來同步發送方和接收方。
  2. 應用場景和優勢
    • UART的簡單性使其非常適合於低速、低成本和低功耗的應用,如簡單的嵌入式系統和微控制器間的通信。
    • USART由於其靈活性和高速能力,更適合於需要高速數據傳輸和更複雜通信要求的場景,例如高性能嵌入式系統、數據收集設備和通信接口。

技術細節的對比 除了通信方式的差異,UART和USART在技術細節上也有所不同。

  1. 時鐘同步
    • UART不需要外部時鐘信號,這意味著硬件設計更簡單,但也導致了數據傳輸率的限制。
    • USART在同步模式下,需要時鐘同步信號,這提高了數據傳輸的速度和效率。
  2. 數據傳輸率
    • UART的數據傳輸率通常較低,但足以應對許多基本的串行通信需求。
    • USART在同步模式下可以達到更高的數據傳輸率,適合數據量大的應用。

UART和USART都是串行通信中的重要組件,它們各有優勢。選擇使用哪一種取決於特定應用的需求。UART在簡單性和成本效益方面占優,而USART則在速度和靈活性方面更勝一籌。

以上是對USART常見的基本觀念。

STM32 USART UART #1 基礎應用 – USART基本概念〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/stm32-usart-uart-1-basic-application-basic-concepts-of-usart/feed/ 0 1987
C# Mutex ReleaseMutex和Close的區別 https://jyouknownit.com/difference-between-releasemutex-and-close-in-csharp-mutex/?utm_source=rss&utm_medium=rss&utm_campaign=difference-between-releasemutex-and-close-in-csharp-mutex https://jyouknownit.com/difference-between-releasemutex-and-close-in-csharp-mutex/#respond Tue, 23 Jan 2024 12:46:11 +0000 https://jyouknownit.com/?p=1979 建立一支程序,如果資源在同一時間只能夠被一支程序或執行序拿來使用,這時通常能夠用Mutex來解決此問題。至於想將資源限定於單一程序或是單一執行序內,使用方式也有所不同,千千百百種此篇不探討。本篇將要來探討,Mutex釋放的時間點,要使用ReleaseMutex來進行釋放,還是Close將Mutex釋放。

C# Mutex ReleaseMutex和Close的區別〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
建立一支程序,如果資源在同一時間只能夠被一支程序或執行序拿來使用,這時通常能夠用Mutex來解決此問題。至於想將資源限定於單一程序或是單一執行序內,使用方式也有所不同,千千百百種此篇不探討。本篇將要來探討,Mutex釋放的時間點,要使用ReleaseMutex來進行釋放,還是Close將Mutex釋放。

Mutex.Close()的使用範例

如果想限定單一程式不能夠重覆執行,或者是多個程序只能夠執行一個,能夠使用以下方式:

  1. 建立Global屬性,名為LockMute的Mutex變數
  2. 能夠在呼叫點寫下LockMutex?.Close()函數
  3. 建立Mutex Instance(此時Mutex已鎖住,任何程序都無法建立相同名稱的Mutex)
  4. 確認Mutex確實建立成功,如不成功則關閉當前程序(可自行定義處理方式)
using System.Threading;

//LockMutex Global Property
Mutex LockMutex {get; set;} = null;
LockMutex?.Close();

LockMutex = new Mutex(true, appName, out bool ret);
if(!ret)
{
  //建立失敗,指定程式已被執行
  //Create Fail!
  //To Do...
  //關閉當前程序
  Environment.Exit(0);
}

Mutex.ReleaseMutex()的使用範例

如何在一個簡單的多線程應用程式中使用 Mutex 來確保對共享資源的安全訪問:

  • mutex.WaitOne():取得控制權
  • mutex.ReleaseMutex():釋放控制權

常見的使用方式,能夠透過多個Thread執行UsrFunction函數,但在同一時間點只會有一個UsrFunction執行//To Do…程序以下的內容,直到執行完成且運行了mutex.ReleaseMutex()函數,才會有下一個UsrFunction繼續執行,依此類推。

void UsrFunction()
{
  try
  {
    mutex.WaitOne();
    //Handle Resources
    //To Do...

    
  }
  finally
  {
    mutex.ReleaseMutex();
  }

}

以上是筆記的內容,簡單來說ReleaseMutex()使用時機是在單一程序內的多執行序的應用方式,而Close()是針對應用程序和應用程序之間的運用,有錯誤麻煩告知~ Thanks!

C# Mutex ReleaseMutex和Close的區別〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/difference-between-releasemutex-and-close-in-csharp-mutex/feed/ 0 1979
STM32 FSMC讀寫SRAM – How to Control SRAM with FSMC https://jyouknownit.com/external-sram-control-with-fsmc/?utm_source=rss&utm_medium=rss&utm_campaign=external-sram-control-with-fsmc https://jyouknownit.com/external-sram-control-with-fsmc/#respond Mon, 22 Jan 2024 12:34:38 +0000 https://jyouknownit.com/?p=646 STM32的FSMC(外部存儲器控制器)是一種專門用於控制外部Memory的控制器,包括SRAM在內。相較於一般的GPIO控制,使用FSMC控制SRAM能夠提高存儲器的速度和容量,並且具有更好的穩定性和可靠性。

STM32 FSMC讀寫SRAM – How to Control SRAM with FSMC〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
Outline

What is SRAM?

SRAM(靜態隨機存取記憶體)是一種高速、低功耗、容量較小的存儲器。與DRAM(動態隨機存取記憶體)不同,SRAM使用靜態電路來保持存儲數據的狀態,因此不需要定期進行刷新操作,可以實現高速的讀寫操作。

SRAM通常用於需要高速讀寫的應用中,例如CPU的高速緩存、實時數據處理、嵌入式系統等。SRAM的速度通常比DRAM快,因為SRAM使用靜態電路存儲數據,並且不需要刷新操作。

SRAM的容量通常比DRAM小,因為SRAM需要使用較多的晶體管來實現靜態電路,而DRAM則可以使用較少的電路實現更大的存儲容量。SRAM通常採用單端口或雙端口設計,具有快速的讀寫速度和低功耗的特點。

總體而言,SRAM是一種高速、低功耗、容量較小的存儲器,通常用於需要高速讀寫的應用中。在嵌入式系統和CPU高速緩存等應用中,SRAM具有重要的地位。

SRAM Control(IS62WV51216BLL-55TLI)

在此篇文章中,會以IS62WV51216BLL-55TLI作為實驗範例,詳細可參考Datasheet

接下來會以4個部分來講解如何控制這顆SRAM,分別為Features-大致描述這顆特別之處,Functional-參考Datasheet並透過圖文方式說明和整理列出要注意的部分,Read/Write Cycle AC Waveforms-以圖文方式說明存取資料時要如何控制Timing。

Outline

SRAM Control – Features

大概描述一下這顆擁有的功能

  • 高速的存取時間最快55ns
  • CMOS 低功耗運作
  • 異步運行(非同步操作)
  • 高、低字節的資料(16-bits)

SRAM Control – Functional

說明關於SRAM的控制訊號

Functional Block Diagram
Functional Block Diagram

在Functional Block Diagram中,分成5個區塊分別來看:

  1. DECODER
    • 負責定位當前輸入/輸出的記憶體位置
    • 圖中Block 512K表示擁有的定址範圍=> 512K = 219(A0-A18)
  2. I/O DATA CIRCUIT
    • 負責將當前輸入/輸出資料進行輸入/輸出的電路
    • 圖中”512K x 16″的16是指,I/O0-I/O7與I/O8-I/O15一共16-bits(頻寬)
  3. CONTROL CIRCUIT
    • 負責判斷是否選擇當前Device/輸入模式/輸出模式/高、低位元的輸入與輸出
  4. COLUMN I/O
    • 根據UB和LB判斷是否將資料進行輸入或輸出
  5. 512K x 16 MEMORY ARRAY
    • 512K x 16-bits表示一共能夠存取最大8M-bits的資料量
Pin Descriptions
Pin Descriptions
Truth Table
Truth Table

根據Functional Block Diagram、Pin Descriptions、Truth Table快速整理出幾點:

  1. 未被選擇(當前SRAM沒有被控制)
    • CS1=> High
    • CS2 => Low
    • LB=> High, UB=> High
  2. 關閉輸出
    • OE=> High, WE=> High
  3. 讀取模式
    • OE=> Low, WE=> High
  4. 寫入模式
    • OE=> X, WE=> Low

PS: CS2 腳位通常拉High,可忽略

SRAM Control – Read Cycle AC Waveforms

Read Cycle Switching Characteristics
Read Cycle Switching Characteristics
Read Cycle No.1
Read Cycle No.1

根據Read Cycle No. 1,以我個人理解此圖想表達,由於讀取資料過程中tRC 必須包含了Address Access Time (tAA, 地址存取時間),也就是這顆SRAM需要tAA時間才能夠將Address 存取進SRAM內,並執行後續動作。

接下來從tAA末端至整體tRC時間內,必須是tRC大於等於tAA的時間(依據Read Cycle Switching Characteristics 的tRC和tAA),其中tAA最多不超過55ns以及tRC至少需要55 ns,如果在55 ns之後輸入了下一個存取資料的地址還有tOHA(10 ns)的時間,能夠取得資料。

但是,通常本人設計會將tRC多保留1個Clock,來保證資料確實被MCU/CPU (or Others)存取成功。(後續說明此用意)

Read Cycle No. 2
Read Cycle No. 2

在圖Read Cycle No. 2中,仔細看能夠觀察出,其實與Read Cycle No. 1無太大的區別,在tRC時間內必須完成Address Access所花費tAA的時間(max 55 ns) ,其中要注意的是過程當中CS1’存取花費時間(tACE1)與tAA是一致的。

再來要注意的部分是,哪一個動作是最後一個執行的,就得以他可能花費最長時間( + 1 Clock) ,才能夠將資料取出來。

假設OE’、LB’和UB’在上個存取週期(n – 1)持續保持Low到當前存取週期(n),只有Chip Select腳位為High(CS1′ => High) ,這時想讀取這SRAM,就得根據Truth Table和Read Cycle Switching Characteristics進行腳位設置與時間上的等待,也就是得將CS1’拉Low之後再等上tACE1 (+ 1 Clock) 時間(55 ns up) ,就能夠將DOUT資料取出來。

SRAM Control – Write Cycle AC Waveforms

Write Cycle Switching Characteristics
Write Cycle Switching Characteristics
Write Cycle No.1
Write Cycle No.1
Write Cycle No.2
Write Cycle No.2
Write Cycle No.3
Write Cycle No.3

以我的理解,在寫入資料過程中能夠分成以下2個重點:

  1. Address Setup Time, 在SRAM中Setup地址所花費的時間
  2. Data Hold Time,將資料寫入到I/O Data後,所持續的時間

Address Setup Time包含了tSA、tHZWE,其中tSA得花費0 ns以上、tHZWE最多可花20 ns;接下來是Data Hold Time包含了tSD、tLZWE,其中tSD得花費25 ns up以確保資料能夠確實寫入進SRAM、tLZWE得花至少5 ns up,確保這次寫入Cycle以結束。

但是因為是異步控制,所以還是整體寫入時序必須等待至少55 ns(tWC),以完成這次的寫入週期,才能夠確保進入到下一個寫入或讀取週期。

Write Cycle No.1Write Cycle No.2Write Cycle No.3都能以我上述方式理解。

Write Cycle No.4
Write Cycle No.4

在圖Write Cycle No. 4中,在連續寫入程序當中,控制UB和LB來分別進行寫入操作,其每次的寫入控制都至少需要tWC時間才能夠完成。

STM32F407 Flexible Static Memory Controller Introduction

Flexible static memory controller (FSMC),中譯為”靈活的”靜態記憶體控制器,接下來介紹在STM32F407的FSMC的功能及如何使用。

以下只介紹關於SRAM控制部分:

Outline

Features

FSMC接口是一個靜態記憶體控制器,能夠存儲如SRAM、ROM、NOR Flash、PSRAM等,也可以接口動態存儲器,如NAND Flash和PC Card等。FSMC的優點有以下幾點:

  • 可以通過配置寄存器來設置不同類型和規格的存儲器,支持多種時序和協議,適應各種存儲器的特性和要求。
  • 可以通過地址映射將外部存儲器映射到STM32的內部地址空間,方便讀寫操作,不需要關心控制線和數據線的時序邏輯
  • 可以支持突發模式訪問同步存儲器,提高數據傳輸效率。
  • 可以通過外部異步等待信號來控制存儲器的訪問速度,適應不同速度的存儲器。
  • 可以通過寫緩衝區來緩衝AHB寫突發事務,加快AHB釋放速度,提高系統性能。

STM32的FSMC功能是一種強大而靈活的外部存儲器擴展方案,可以幫助開發者實現更多的功能和性能。

Block Diagram

FSMC能夠分成4類,分別為:

  1. AHB介面(包含Configuration Register)
  2. NOR/PSRAM介面控制器
  3. NAND/PC Card介面控制器
  4. 外部設備介面
FSMC Block Diagram

AHB Interface

根據Reference手冊:

AHB 介面能夠使內部 CPU 和其他總線主外設能夠訪問外部Static Memories。

透過FSMC能夠將AHB所傳送的資料轉換成外設可以使用的協議。比較特殊的是,當外部所接Memory是16或8位元的,AHB傳輸位元寬是32位元且能夠分割成16或8位元來存取。

FSMC Chip Select (FSMC_NEx)不會在連續訪問之間切換,除非啟用了擴展模式的D模式情況下才會發生。

FSMC的參考Clock與AHB Clock(HCLK)相同。

Device Memory Mapping

FSMC Memory Banks
FSMC Memory Banks

在FSMC中,連接至外部Memory的空間分割成4組固定大小Banks,每組Bank大小為256Mbyte。(可參考FSMC Memory Banks)

其中Bank 1分割成四組可用來控制NOR Flash或是PSRAM的Memory。選擇致其中一組的方式是用Chip Select(NEx)腳位決定。

四組分別為:

  • Bank 1 – NOR/PSRAM 1 (0x6000_0000 ~ 0x63FFF_FFFF, 64MB)
  • Bank 1 – NOR/PSRAM 2 (0x6400_0000 ~ 0x67FFF_FFFF, 64MB)
  • Bank 1 – NOR/PSRAM 3 (0x6800_0000 ~ 0x6BFFF_FFFF, 64MB)
  • Bank 1 – NOR/PSRAM 4 (0x6C00_0000 ~ 0x6FFFF_FFFF, 64MB)
NOR/PSRAM bank selection
NOR/PSRAM bank selection
NOR/PSRAM bank selection
NOR/PSRAM bank selection

根據圖NOR/PSRAM bank selection,HADDR[27:26]是內部AHB地址傳輸線位置,能夠轉換成外部Memory位置。

External memory address
External memory address

根據圖External memory address,以我理解來表達,當外部Memory數據位元寬為8-bit,內部AHB HADDR[25:0]剛好能夠對應至外部Memory的地址位置,所以最大存取資料量為64 Mbyte x 8=512 Mbit;當外部Memory數據位元寬為16-bit,如過透過內部一對一地址來存取資料,會造成UB’或LB’無法取得,所以內部HADDR[25:0]會在要存取16-bit資料位元寬的時候,將內部HADDR[25:1]對應至外部Memory[24:0]地址位置(HADDR[0]不接),接下來當上層存取資料(16-bit)時,這樣做就能夠使一個位置能夠存取到16-bit位元寬的資料。

Programmable Access Parameters

Programmable NOR/PSRAM access parameters
Programmable NOR/PSRAM access parameters

Device 所用參數:

  • Address Setup:設定地址過程中的時間
  • Address Hold:地址需要持續的時間
  • Data Setup:設定資料過程中的時間
  • Bust Turn:讀寫狀態改變所需的時間

PSRAM controller asynchronous transactions

Asynchronous static memories

Mode 1 – SRAM/PSRAM (CRAM)
Mode 1 read accesses
Mode 1 read accesses
Mode 1 write accesses
Mode 1 write accesses

注意在Mode1 寫入模式中,當NWE訊號上拉時,Address和Data能夠維持至少1 HCLK的時間,以確保資料能夠正確寫入。


Mode A – SRAM/PSRAM (CRAM) OE toggling
ModeA read accesses
ModeA read accesses
ModeA write accesses
ModeA write accesses

在ModeA寫入模式中,與Mode 1不同之處在於ModeA的讀取和寫入能夠將時序分別設置。

Mode D – asynchronous access with extended address
Mode D read accesses
Mode D read accesses
ModeD write accesses
ModeD write accesses

在ModeD寫入模式中,與Mode 1不同之處在於ModeD的NADV訊號結束號,能夠設置一段時間,之後再進行資料寫入。(本篇使用SRAM用不到NADV訊號)

STM32F407 FSMC Control SRAM Experiment

在了解FSMC和SRAM之後,接下來要透過實驗使用FSMC來讀寫SRAM,來更加理解FSMC的使用方式。

Outline

  • 實驗目的
  • 實驗順序
  • 實作&結果
  • 結論

實驗目的

透過STM32F407ZGT6利用FSMC控制SRAM,再透過調整內部時序相關參數,查看讀寫是否成功。

實驗順序

  1. 利用STM32 CubeMX設置與說明
  2. 測試實驗組與對照組
    • 對照組測試與結果
    • 實驗組
      • Address setup time 3 clock cycle測試與結果
      • Address setup time 2 clock cycle測試與結果
      • Address setup time 1 clock cycle測試與結果

實作&結果

SRAM-IS62WV51216BLL-55TLI電路圖

根據圖SRAM-IS62WV51216BLL-55TLI電路圖,相關控制方式可至文章上方SRAM Control(IS62WV51216BLL-55TLI)查看。

System Core
RCC Mode and Configuration
Clock Configuration

選擇System Code – RCC,設置RCC Mode – High Speed Clock (HSE)為Crystal/Ceramic Resonator,接下來到Clock Configuration畫面,設置Input Frequency為8 MHz,PLL Source Mux選擇HSE,設置System Clock Mux為PLLCLK,這樣Clock就設定完成了。

這裡HCLK我選擇設置成64 MHz,換算成週期為15.625 ns。 (其實還能夠更快,只是目前我的邏輯分析儀Sample rate只能夠支持到100M)

Connectivity
FSMC Mode-1
FSMC Mode-2
FSMC Configuration

選擇FSMC並在Mode進行設定:

  • Chip Select選擇NE3
  • Memory Type選擇SRAM
  • Address選擇19bit
  • 啟用Byte enable.

Configuration – Control:

  • Memory Type選擇SRAM
  • Bank選擇Bank 1 NOR/PSRAM 3
  • 啟用Write operation
  • 禁用Extended mode

Configuration – timing:

  • 將Address setup time in HCLK clock cycles設置4
  • 將Data setup time in HCLK clock cycles設置1
  • 將Bus turnaround time in HCLK clock cycles設置0
  • 預設Access mode會為ModeA

測試實驗組與對照組

不變條件: Address Hold Time、Data Setup Time

控制變因: Address Setup Time

下面為測試結果

對照組測試與結果

將數值”0x1234″寫入至SRAM_Stream + 0位置以及”0x5678″寫入至SRAM_Stream + 0位置,與讀取數值一致。

實驗組

Address setup time 3 clock cycle測試與結果

將數值”0x1234″寫入至SRAM_Stream + 0位置以及”0x5678″寫入至SRAM_Stream + 0位置,與讀取數值一致。

Address setup time 2 clock cycle測試與結果

將數值”0x1234″寫入至SRAM_Stream + 0位置以及”0x5678″寫入至SRAM_Stream + 0位置,與讀取數值一致。

Address setup time 1 clock cycle測試與結果

將數值”0x1234″寫入至SRAM_Stream + 0位置以及”0x5678″寫入至SRAM_Stream + 0位置,讀取數值皆為0,測試失敗。

結論

討論當Address Setup Time設置1,Address Hold Time設置1,存取時間為Timing.AddressSetupTime + Timing.AddressHoldTime = 2 clock cycle = 15.625 ns * 2 = 31.25 ns。接下來需要參考圖FSMC ModeA read accesses和圖FSMC ModeA write accesses,說明可能存取失敗的原因如下:

  1. 根據圖Read Cycle Switching Characteristics,Max tAA(Address Access Time) is 55 ns,意思是地址存取時間最多為55ns,小於55ns則不保證存取能夠成功。
  2. 根據圖Write Cycle Switching Characteristics,Min tPWE(WE’ Pulse Width) is 40 ns,但是Timing.AddressHoldTime is 1 clock cycle is 15.625 ns,15.625 ns無法確保這麼短的時間內,能夠將資料正確寫入。

目前先以Datasheet和Reference Manual推測失敗的原因,未來找時間以邏輯分析儀來分析存取失敗的原因。

Manage External SRAM

在外部SRAM存取實驗中,發現如果每次要針對位置進行存取資料,是相當麻煩的事情,於是要用到C語言高階語言的特性,來進行有效的SRAM管理,不必再因為得想地址位置而感到煩惱。

Manager SRAM Address Example

以上圖Manager SRAM Address Example為例,首先建立一個結構SRAM_Data,接著利用Mapping的方式將Address映射成SRAM_Data結構(sram_data指標),在存取sram_data指標進行讀寫資料時也能夠存取到外部的SRAM了,且每次存取不用再透過地址轉換讀取資料,大幅增加可讀性與操作性

Conclusion

最後總結,對於FSMC還有許多需要學習的部分,像是對於NOR/NAND Flash的存取、LCD TFT控制、PC Card等等,或是對於不同模式A、B、C和模式D的實際應用,未來一定給自我學習的機會,把這些功能學習起來。

再來對於實驗部分希望再嚴謹一些,有時間把FSMC控制過程的訊號透過邏輯分析儀量測出來,與目前推論失敗的原因做個比對和糾正。

Reference

STM32 FSMC讀寫SRAM – How to Control SRAM with FSMC〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/external-sram-control-with-fsmc/feed/ 0 646
STM32 ADC #4 基礎應用 – Analog Watchdog (AWD, 模擬看門狗) – ADC AWD 實驗 https://jyouknownit.com/stm32-adc-4-basic-applications-analog-watchdog-awd-adc-experiment/?utm_source=rss&utm_medium=rss&utm_campaign=stm32-adc-4-basic-applications-analog-watchdog-awd-adc-experiment https://jyouknownit.com/stm32-adc-4-basic-applications-analog-watchdog-awd-adc-experiment/#respond Sat, 20 Jan 2024 09:01:35 +0000 https://jyouknownit.com/?p=1949 本文介紹了STM32 ADC使用類比看門狗(AWD)的基本方法。從AWD的初步使用開始,涵蓋了實驗流程的規劃、電路設計以及AWD設定。最後,通過實際實驗驗證了當電壓超出預定範圍時,AWD會觸發中斷函數以應對當前情況。

STM32 ADC #4 基礎應用 – Analog Watchdog (AWD, 模擬看門狗) – ADC AWD 實驗〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
本文介紹了STM32 ADC使用類比看門狗(AWD)的基本方法。從AWD的初步使用開始,涵蓋了實驗流程的規劃、電路設計以及AWD設定。最後,通過實際實驗驗證了當電壓超出預定範圍時,AWD會觸發中斷函數以應對當前情況。

Outline

STM32 ADC Analog Watchdog (AWD) 介紹

以下以reference manual來說明

如果ADC轉換的模擬電壓低於低閾值(LTR)或高於高閾值(HTR),則模擬看門狗(AWD)狀態位將被設置。這些閾值在ADC_HTR和ADC_LTR 16位寄存器的12個最低有效位中進行編程。可以通過在ADC_CR1寄存器中使用AWDIE位來啟用中斷。

閾值設定與在ADC_CR2寄存器中由ALIGN位選擇的對齊方式無關。比較動作在對齊之前進行。

模擬看門狗可以通過配置ADC_CR1寄存器,在一個或多個通道上啟用。

Analog watchdog guarded ares
Analog watchdog guarded ares
ADC control register 1 (ADC_CR1)
ADC control register 1 (ADC_CR1)

AWDIE:模擬看門狗中斷啟用 此位由軟件設定和清除,用於啟用/禁用模擬看門狗中斷。

0:模擬看門狗中斷禁用

1:模擬看門狗中斷啟用

以上是對AWD的筆記。

STM32 ADC AWD 實驗規劃

針對AWD實驗,規劃了實驗流程如下:

首先啟用ADC的Continuous Conversion Mode,硬體能夠不斷地觸發ADC的轉換,接下來設置LED長亮狀態,透過AWD不斷的檢查當前ADC內的ADC_DR是否超出設置的HTR或LTR,如果超出LED閃爍5次,反之持續LED維持長亮。

AWD Experiment Flow Chart
AWD實驗流程圖

GPIO設定當AWD中斷發生以閃爍LED警告。

Alert Led Settings
Alert Led設定

下面是設置ADC Configuration的參數,需要注意到Continuous Conversion Mode需要被Enable的,High/Low Threshold也需要設置,

分別為3473(約2.8 Volt = 3479 x 0.000806 volt)和1241(約1.0 Volt = 1241 x 0.000806 volt),最後WatchDog Interrupt也需要Enable。

ADC Configuration Settings
ADC Configuration Settings

ADC1的global interrupts中斷是需要Enable的,如下:

Enable ADC1 global interrupts
Enable ADC1 global interrupts

在本次實驗的實驗電路如下,高電位3.3 Volt、低電位0 Volt,下方可變電阻約為48k歐姆,上方進行調整可變電阻(0 ~ 250k歐姆)來觀察此次AWD實驗。當超過或低於Analog watchdog guarded ares時,LED會進行閃爍。

AWD Experiment Circuit Diagram
AWD 實驗電路圖

以上完成之後,接下來看看實驗結果。

STM32 ADC AWD 實驗結果

Global變數宣告

  • u32_ADC_DR:用來暫存ADC_DR的數值
  • IN0_Voltage:用來暫存ADC_DR轉換成電壓的值
/* USER CODE BEGIN PV */
uint32_t u32_ADC_DR;
float IN0_Voltage; //單位: volt
/* USER CODE END PV */

ADC1初始化

  • ADC_Enable:啟用ADCON
  • HAL_ADCEx_Calibration_Start:校正指定ADC
  • HAL_ADC_Start:開始測量轉換類比訊號。
/* USER CODE BEGIN ADC1_Init 2 */
ADC_Enable(&hadc1);
HAL_ADCEx_Calibration_Start(&hadc1);
Usr_AsmDelay_us(2);
HAL_ADC_Start(&hadc1);
/* USER CODE END ADC1_Init 2 */

AWD中斷函數(需實現HAL_ADC_LevelOutOfWindowCallback)

Analog watchdog callback in non blocking mode.

當發生Level Out Of Window的情況,將會執行以下程序:

/* USER CODE BEGIN 4 */
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc);
  /* NOTE : This function should not be modified. When the callback is needed,
            function HAL_ADC_LevelOutOfWindowCallback must be implemented in the user file.
  */
	if(hadc->Instance == hadc1.Instance)
	{
		//User To Do...
		int alartCnt = 0;
		do
		{
			HAL_GPIO_TogglePin(AlartLed_GPIO_Port, AlartLed_Pin);
			
			Usr_AsmDelay_ms(100);
			
		}while(alartCnt++ < 10);
		
		HAL_GPIO_WritePin(AlartLed_GPIO_Port, AlartLed_Pin, GPIO_PIN_RESET);
	}
}
/* USER CODE END 4 */

Main函數

在while loop中,不斷地將ADC_DR資料放置u32_ADC_DR中,並換算實際電壓數值放置IN0_Voltage。

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    u32_ADC_DR = hadc1.Instance->DR;
    IN0_Voltage = u32_ADC_DR * 0.000806f;
  }
  /* USER CODE END 3 */

下方是AWD實驗影片,當上方電阻值變大,ADC量測值將會小於1 Volt,影片中右手方的Led會開始閃爍,直到上方電阻值變小。

下方是ADC量測值1659,在Analog watchdog guarded ares範圍內(1241 ~ 3473),並未觸發中斷導致Led閃爍。

in Analog watchdog guarded ares

下方是ADC量測值1218,在Analog watchdog guarded ares範圍內(1241 ~ 3473),並觸發中斷導致Led閃爍。

out of Analog watchdog guarded ares

結論

在這篇學習了AWD的使用方式,並透過基礎的電路實驗證明了AWD的作用,以及程式碼的撰寫方式,提供各位學習STM32 ADC AWD的使用者們。

STM32 ADC #4 基礎應用 – Analog Watchdog (AWD, 模擬看門狗) – ADC AWD 實驗〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/stm32-adc-4-basic-applications-analog-watchdog-awd-adc-experiment/feed/ 0 1949
STM32 ADC #3 基礎應用 – Continuous Conversion Mode (連續轉換模式) https://jyouknownit.com/stm32-adc-3-basic-application-continuous-conversion-mode/?utm_source=rss&utm_medium=rss&utm_campaign=stm32-adc-3-basic-application-continuous-conversion-mode https://jyouknownit.com/stm32-adc-3-basic-application-continuous-conversion-mode/#respond Thu, 18 Jan 2024 15:47:40 +0000 https://jyouknownit.com/?p=1915 本文將深入探討STM32 ADC的Continuous Conversion Mode(連續轉換模式)。這種模式,正如其名,支持連續的資料轉換。文章首先解釋連續轉換模式的概念、操作方法以及資料提取過程。其次,我們將探討Scan Mode與連續轉換模式之間的關聯,並設置如何透過DMA將資料存放至SRAM內。瞭解這些功能後,我們會設計一個實驗來展示連續轉換模式的應用,並通過程式碼與實驗電路來完成此實驗。此篇的目的是為了確保讀者能夠有效利用此模式,同時避免任何非預期的結果。

STM32 ADC #3 基礎應用 – Continuous Conversion Mode (連續轉換模式)〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
本文將深入探討STM32 ADC的Continuous Conversion Mode(連續轉換模式)。這種模式,正如其名,支持連續的資料轉換。文章首先解釋連續轉換模式的概念、操作方法以及資料提取過程。其次,我們將探討Scan Mode與連續轉換模式之間的關聯,並設置如何透過DMA將資料存放至SRAM內。瞭解這些功能後,我們會設計一個實驗來展示連續轉換模式的應用,並通過程式碼與實驗電路來完成此實驗。此篇的目的是為了確保讀者能夠有效利用此模式,同時避免任何非預期的結果。

Outline

ADC 連續轉換模式介紹

在連續轉換模式下,ADC一完成一次轉換就立即開始另一次。這種模式可以通過外部觸發啟動,或者在ADC_CR2寄存器中設置ADON位,同時CONT位為1。 每次轉換完成後:

  • 如果轉換的是regular channel:
    • 轉換後的數據存儲在16位的ADC_DR寄存器中
    • EOC(轉換結束)標誌被設置
    • 如果設置了EOCIE,則會生成一個中斷。
  • 如果轉換的是injected channel:
    • 轉換後的數據存儲在16位的ADC_DRJ1寄存器中
    • JEOC(注入轉換結束)標誌被設置
    • 如果設置了JEOCIE位,則會生成一個中斷。

Scan Mode介紹

此模式用於掃描一組analog channel。 通過設置ADC_CR1寄存器中的SCAN bit來選擇掃描模式。一旦設置了,ADC就會掃描在ADC_SQRx寄存器中選擇的所有通道(for regular channel)或在ADC_JSQR中選擇的通道(injeceted channel)。對組中的每個通道進行單次轉換。每次轉換結束後,該組的下一個通道會自動轉換。如果設置了CONT bit,轉換不會在選擇的最後一個組通道處停止,而是會從第一個選擇的組通道重新開始。 使用掃描模式時,必須設置DMA bit,並使用直接內存訪問控制器在每次更新ADC_DR寄存器後將常規組通道的轉換數據傳輸到SRAM。 注入通道的轉換數據總是存儲在ADC_JDRx寄存器中。

ADC 連續轉換模式實驗規劃

實驗目的: 熟悉ADC連續轉換程式碼應用之方式,並利用DMA將資料放置指定位置。

實驗描述: 以軟體觸發方式,進行ADC的轉換,再透過DMA將ADC_DR將放置指定位置,以避免資料覆蓋。

實驗條件:

以ADC1進行實驗,設定參數如下圖

ADC Configuration
ADC Configuration

在DMA Settings中需要注意一點,DMA中斷需要關閉。(如將Interrupt開啟會導致STM32會不斷處理發生中斷的函數內容)

DMA Settinigs
DMA Settings
Disable DMA Interrupt
Disable DMA Interrupt功能

實驗電路圖:

下面是一個串聯電路的電路圖,其量測點分別在兩電阻之間(IN1)和地(IN0)。

Experimental Circuit Diagram
實驗電路圖

接下來看看程式碼和實驗的結果。

ADC 連續轉換模式實驗結果

程式碼:

在CubeMX專案建立完成後,需加入的程式碼如下

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct
{
	uint32_t ADC;
	float Volt;
}IN_CH;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define ADC1_CH_SIZE 2
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint32_t u32_ADC_Data[ADC1_CH_SIZE] ;
IN_CH ADC_IN[ADC1_CH_SIZE];
/* USER CODE END PV */

/* USER CODE BEGIN PFP */
void Cal_ADC_Values(IN_CH*, uint16_t);
/* USER CODE END PFP */

/* USER CODE BEGIN 4 */
void Cal_ADC_Values(IN_CH* IN_X, uint16_t ADC_Value)
{
	IN_X->ADC = ADC_Value;
	IN_X->Volt = ADC_Value * 0.000806f;
}
/* USER CODE END 4 */

需要將上述程式碼放置Global的指定位置。其中IN_CH結構是方便在程式碼中呼叫和查看的數據結構,等等觀察結果能夠一次性的看出量測出ADC的值和換算成電壓的值。Cal_ADC_Values函數是用來保存和計算ADC Channel所量測出來的數值。

下方是ADC初始化函數的內容:

/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 2;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
	HAL_ADCEx_Calibration_Start(&hadc1);
	Usr_AsmDelay_us(2);
	HAL_ADC_Start_DMA(&hadc1, u32_ADC_Data, ADC1_CH_SIZE);
  /* USER CODE END ADC1_Init 2 */

}

在上方ADC初始化函數中,第54行是HAL函數庫啟動ADC開始量測的函數,其不同之處是在ADC1轉換完成之後會將在ADC_DR中的數值透過內部硬體DMA的方式將資料傳至指定的SRAM位置中(u32_ADC_Data)。

下方是main函數內部需修改的部分。

/* USER CODE BEGIN 2 */
uint16_t* CIN = (uint16_t*)u32_ADC_Data;
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    Cal_ADC_Values(ADC_IN + 0, CIN[0]);
    Cal_ADC_Values(ADC_IN + 1, CIN[1]);
}
/* USER CODE END 3 */

在main函數中將u32_ADC_Data轉換成16 bit的陣列。(寫法有很多種方式,沒有一定)

在while迴圈中將量測的值和ADC_IN數據結構位置個別帶入Cal_ADC_Values函數內,用於計算電壓值和儲存ADC數值。

接下來看看實驗結果。

Values of two channels measured through ADC1 and the voltage values upon completion of conversion
透過ADC1量測的兩個通道的數值和轉換完成的電壓值

注意到在ADC此模式中,一旦啟動了就會不段的運行,時時刻刻的將ADC中的每個channel的數值更新到SRAM內,且能夠將其作運用,以達到不斷監視的作用等等。

結論

在本篇文章中,介紹了STM32 ADC Continuous Conversion Mode的使用方式,並且規劃了簡單易懂的實驗內容,利用ADC1透過兩個Channnel執行參數的設置,最後在實驗部分確實的展現出實驗的結果,其中還強調需要注意的部分如:DMA Settings中,為何Interrupt需要關閉等等。

在學習此篇文章的內容,希望程序猿們需要理解的部分:

  • STM32 ADC Continuous Mode/Scan Mode的關係
  • STM32 DMA的概念
  • 以程式碼實現Continuous Mode並利用DMA將數據傳至指定的SRAM內

以上細節可參考reference manual以加深學習內容。

STM32 ADC #3 基礎應用 – Continuous Conversion Mode (連續轉換模式)〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/stm32-adc-3-basic-application-continuous-conversion-mode/feed/ 0 1915
STM32 ADC #2 基礎應用 – Single conversion mode https://jyouknownit.com/adc-2-basic-applications-single-conversion-mode-stm32f103rct6/?utm_source=rss&utm_medium=rss&utm_campaign=adc-2-basic-applications-single-conversion-mode-stm32f103rct6 https://jyouknownit.com/adc-2-basic-applications-single-conversion-mode-stm32f103rct6/#respond Tue, 16 Jan 2024 08:32:41 +0000 https://jyouknownit.com/?p=1878 在本篇文章中會講解STM32 ADC Single Conversion Mode的基礎應用,首先會介紹ADC單次轉換模式以及如何操作並透過Data Register取得ADC轉換好的資料。接者規劃本次的實驗方式與步驟,其中包含ADC參數的設定和如何計算ADC轉換所需要的時間,最後按照實驗步驟將結果實驗出來,並說明如何增加ADC量測的準確性。

STM32 ADC #2 基礎應用 – Single conversion mode〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
在本篇文章中會講解STM32 ADC Single Conversion Mode的基礎應用,首先會介紹ADC單次轉換模式以及如何操作並透過Data Register取得ADC轉換好的資料。接者規劃本次的實驗方式與步驟,其中包含ADC參數的設定和如何計算ADC轉換所需要的時間,最後按照實驗步驟將結果實驗出來,並說明如何增加ADC量測的準確性。

Outline

ADC單次轉換模式介紹

簡單來說,ADC單次轉換模式是指,ADC單次轉換完成後數據存至暫存器內,再透過DMA或是中斷方式讓使用者抓取數據。

ADC單次轉換流程

如何開始ADC的量測,首先需設置ADC_CR2中的ADON暫存器(for a regular channel only),或是透過外部觸發的方式(for a regular or injected channel)來開始進行ADC的轉換,同時CONT需Reset。

Regular Channel轉換完成後,取得數據方式:

  1. 數據存至16-bit ADC_DR內
  2. EOC flag會被設置
  3. 如果有設置EOCIE則會發生中斷

Injected Channel轉換完成後,取得數據方式:

  1. 數據存至16-bit ADC_DRJ1內
  2. JEOC flag會被設置
  3. 如果有設置JEOCIE則會發生中斷

以上完成後,ADC將會停止動作。

上述是ADC單次轉換的流程。

ADC單次轉換模式實驗規劃

軟體觸發ADC轉換

實驗目的: 熟悉ADC單次轉換程式碼應用之方式,並計算ADC轉換所需的時間。

實驗描述: 以軟體觸發方式,進行ADC的轉換,最後從ADC_DR將資料取出,且與電表做對比。

實驗條件

  1. 設定ADC1中的IN0
  2. Data Alignment : Right alignment
  3. Scan Conversion Mode : Disable
  4. Continuous Conversioin Mode : Disable
  5. Discontinuous Conversioin Mode : Disable
  6. Enable Regular Conversions : Enable
  7. Number of Conversion : 1
  8. External Trigger Conversion Source : Regular Conversion launched by software
  9. ADC Clock : 8 MHz
ADC Parameter Settings
ADC參數設定
ADC Clock Settings
ADC Clock 設定

評估轉換時間

根據下方reference manual的公式,Tconv = sample time + 12.5 cycles = 1.5 cycles + 12.5 cycles = 14 cycles 一共有14個週期,換算成時間為14 / 8e6 = 1.75 微秒,大約2 us時間能夠把資料取出。

ADC Sample Time計算公式

實驗環境

下方是實驗電路圖,在圖的上方是正電壓、下方為負電壓、電位差為3.3 Voltage,中間以兩顆可調式電阻串聯,ADC和三用電表量測點以兩顆電阻之間對地的量測。

Experimental Circuit Diagram
實驗電路圖

ADC單次轉換模式實驗結果

軟體觸發ADC轉換

說明實驗程式碼

ADC Resolution為12bit,所以每個bit約為0.806 uVolt/bit。

其中測試結果能夠發現,量測值並不是非常準確。在透過校正後與實際量測值更加接近了(2.075 Volt)。

void Usr_AsmDelay_us(uint32_t us)
{
	if(!Asm_Assert()) Asm_Delay_Error();
	uint32_t val = us * _usAsmTick;
	while(--val);
}
HAL_StatusTypeDef eStatus;
uint32_t data = 0x0000;
float mVolPerBit = 0.806;
float totalmVol = 0.0f;
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
    /* USER CODE END WHILE */
		
    /* USER CODE BEGIN 3 */
    eStatus = HAL_ADC_Start(&hadc1);
    Usr_AsmDelay_us(2);
    HAL_ADC_Stop(&hadc1);
    data = ADC1->DR;
    totalmVol = mVolPerBit * data;
}
/* USER CODE END 3 */
Multimeter Measurement Value: 2.17 Volts
三用電表量測值 2.17 Volt
Uncalibrated ADC Measurement Value (1.99 Volts)
未校正ADC量測值(1.99 Volt)
Calibrated ADC Measurement Value (2.075 Volts)
校正後ADC量測值 (2.075 Volt)

為什麼ADC需要校正?

簡單來說,ADC有內部自我校正功能。能夠減少寄生電容所帶來的量測誤差。會計算每個電容的誤差值並在每次ADC轉換後扣除電容誤差。校正時機點是在ADC Power-ON的時候進行校正,須注意校正過程需兩個ADC Clock Cycles的時間。

Calibration Reference Manual
Calibration Reference Manual

下面是ADC初始化完成後進行校正所呼叫的API。

static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
	//sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
  HAL_StatusTypeDef calStatus = HAL_ADCEx_Calibration_Start(&hadc1);
  if(calStatus != HAL_OK)
  {
    while(1);
  }
  /* USER CODE END ADC1_Init 2 */

}

下一步嘗試使用28.5Cycles Sample 量測值會更精確(2.157 Volt),但花費時間需約Tconv = 5.125 us ~= 6 us。

HAL_StatusTypeDef eStatus;
uint32_t data = 0x0000;
float mVolPerBit = 0.806;
float totalmVol = 0.0f;
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
    /* USER CODE END WHILE */
		
    /* USER CODE BEGIN 3 */
    eStatus = HAL_ADC_Start(&hadc1);
    //Usr_AsmDelay_us(2);
    Usr_AsmDelay_us(6);
    HAL_ADC_Stop(&hadc1);
    data = ADC1->DR;
    totalmVol = mVolPerBit * data;
		
    Usr_AsmDelay_ms(100);
}
/* USER CODE END 3 */
After Calibration and Increasing Sample Time (28.5 Cycles), ADC Measurement Value is 2.157 Volts
校正後且增加Sample Time(28.5 Cycles) ADC量測值為2.157 Volt

還用多種Sample Time設定值,就給各位去試試囉~

Explanation of the ADC Sample Time Register
ADC sample time register說明

結論

本實驗旨在深入理解ADC(模數轉換器)單次轉換過程。特別關注的是轉換過程中的程式碼撰寫方法,以及轉換時間計算的重要性。此外,本研究還探討了Sample Time選擇對ADC量測準確性的影響,並指出在ADC Power-ON時進行校正對提高量測精度的益處。

如有錯誤部分!幫忙留言或寫信通知我。謝謝!

STM32 ADC #2 基礎應用 – Single conversion mode〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/adc-2-basic-applications-single-conversion-mode-stm32f103rct6/feed/ 0 1878
STM32 ADC #1 基礎功能介紹與重點整理 https://jyouknownit.com/what-is-adc1-stm32-stm32f103rct6/?utm_source=rss&utm_medium=rss&utm_campaign=what-is-adc1-stm32-stm32f103rct6 https://jyouknownit.com/what-is-adc1-stm32-stm32f103rct6/#respond Thu, 28 Dec 2023 14:52:52 +0000 http://jyouknownit.com/?p=201 本文是根據STM32 ADC參考手冊的重點整理與基礎介紹。理解其中的Signal、Continuous Conversion和Scan、Discontinuous不同模式的運行方式,以及

STM32 ADC #1 基礎功能介紹與重點整理〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
可根據Reference Manual(RM0008 Page.215),ADC全名是Analog-to-Digital Converter,中文是類比轉數位轉換器。

Outline

ADC Introduction

在下圖Figure-1中,整理出:

  • 使用的是12-bit Resolution的ADC。
  • ADC是successive approximation類型的。
  • 一組ADC能夠依序Measure最多18組的Channel,其中有16個Channel是外部的、2個Channel是內部的。
  • 它擁有4種模式Signal、Continuous、Scan、Discontinuous。
  • 還擁有Analog Watchdog,來偵測輸入電壓是否高於最大值或是小於最小值。
  • ADC Clock是由PCLK2分頻而來的,其不能夠超過14MHz。

ADC main features

接下來會稍微解釋在圖Figure-2中,ADC的主要功能,如下:

12-bit resolution.

ADC會將量測出的結果存於12個bit中,假設 0V <= Vin <= 3.3V,3.3V / (212 – 1) = Volt per step ~= 0.806 mV per step,在12-bit中假如值是4000,則ADC量測到的電壓為4000 x 0.806 mV = 3.2 V。

Interrupt generation at End of Conversion, End of Injected conversion.

不管在Regular或是Injected的轉換結束後,都能夠產生中斷。

Interrupt generation at Analog watchdog event.

當Analog Watchdog event發生,能夠產生中斷。

Signal and continuous conversion modes.

Signal conversion mode是在所選擇的Channel 有一次EOC的事件,並產生Interrupt。

Continuous conversion mode是ADC會連續的轉換,也就是所有選擇到的Channel都會進行轉換,且在每個EOC之後能夠有Interrupt產生。

Scan mode for automatic conversion of channel 0 to channel ‘n’.

當Scan mode被設置時,在Register所選擇之Channels的轉換會依序的轉換下去。(詳細解釋在文章後面)

Self-calibration.

內建校正功能,透過校正能夠提升量測的準確率。

Data alignment with in-built data coherency.

在16-bit Register中,其中有12-bit是量測值,其能夠向左對齊或向右對齊。

Channel by channel programmable sampling time.

可程式化取樣頻率。

External trigger option for both regular and injected conversion.

Regular和Injected的轉換可透過外部觸發。

Discontinuous mode.

與Continuous mode相反,每次的觸發轉換的轉換數量,由使用者決定。

Dual mode (on devices with 2 ADCs or more).

同時間可多個ADC進行轉換。

ADC conversion time.

ADC Clock 是由 PCLK2 分頻,分頻只能夠是2的倍數(2、4、6…),所以當 PCLK2 是 56 MHz,則最快能夠是14 MHz ( = 56 / 4 ),但是當 PCLK2 是72 MHz,ADC Clock最快只能夠是12 MHz ( = 72 / 6 )。(至於為什麼當ADC Clock是14 MHz時轉換時間是1 uS,而 ADC Clock 是12 MHz的時候是1.17 uS,文章後面會在解釋。)

ADC supply requirement: 2.4 V to 3.6 V.

啟用ADC功能必須以2.4 V到3.6 V之間的電壓。

ADC input range: VREF- <= VIN <= VREF+.

ADC量測電壓需大於等於VREF-和小於等於VREF+

DMA request generation during regular channel conversion.

須注意到,在Regular Channel轉換完畢之後,都是儲存於一個獨立的16-bit Register內,所以在連續轉換的情況下,需要以啟用DMA功能將每一筆資料存於使用者指定的地方。

Note: Interrupt和Event區別,以我目前理解為有Event不一定有Interrupt,但有Interrupt一定有Event發生。


Single ADC block Diagram

接下來會對ADC block Diagram加以說明。

在圖Figure-3中,主要分成六個部分,分別為Analog Input、Triggers、ADC、Interrupt、Analog Watchdog、Data Registers。

下方Table是ADC Pins的說明:

  • VREF+,是輸入類型類比訊號,條件 2.4 V <= VREF+ <= VDDA
  • VDDA,是輸入類型類比訊號,條件 2.4 V <= VDDA <= 3.6 V。
  • VREF-,是輸入類型類比訊號,條件 VREF- = VSSA
  • VSSA,是輸入類型類比訊號,條件 VSSA = VSS
  • ADCx_IN[15:0],類比訊號,最多有21個類比Channel。
Table-1 Analog Signal Type & Remarks

Analog Inputs

在下方圖Figure-4以紅框1.中:

ADCx_IN’n’是外部輸入的Analog Signal,輸入至GPIO Ports,再經由Analog MUX輸出給Analog to digital converter。除了外部輸入外,還有兩個內部Analog Signal,分別為Temp. sensor以及VREFINT

ADC Block

ADC

在Figure-5紅框2.中,前面經由Analog MUX,最多可將4個Channel配置成Injected channels、最多16個Channel配置成Regular channels。

其中左上方有4條Signal輸入,分別為VREF+、VREF-、VDDA、VSSA,詳細描述在上方的Table-1中。

ADCCLK來自ADC prescaler再輸入到Analog to digital converter。

還能夠有DMA的請求,直接透過硬體放置到指定的空間。

Triggers

在Figure-6紅框3.中,是Injected Channel的Trigger block擁有外部Trigger EXTI_15以及Timer來Trigger Injected channels的轉換動作。

同樣Figure-6紅框4.中,是Regular Channel德Trigger block擁有外部Trigger EXTI_11以及Timer來Trigger Regular channels的轉換動作。

Data Registers

在Figure-6 ADC block中的Injected(5.) / Regular(6.) Channels轉換出的Data,分別存於Figure-7 Injected data registers以及Regular data register。這裡心細的人就會注意到,Injected channels能夠儲存的register一共有四組(4 x 16-bits),但是Regular channels卻只能夠使用一組的register,這部分會在文章後面,會提到可透過DMA或其他方式,將在Regular channels多個Channel的轉換出的值存於指定空間。

Interrupts

在Figure-8 紅框7.中,是ADC Interrupt block,可看出在圖下方Injected data registers和Regular data register,轉換完成部分分別有EOC和JEOC兩部分,其中Injected或是Regular data register(s)轉換完成之後,都會產生EOC,但是JEOC是針對Injected channels才擁有的Interrupt。

在Figure-8 籃框8.,是Analog watchdog block,有兩組Register分別為High Threshold和Low Threshold主要是用來偵測輸入電位,不能夠高於High Threshold和不能夠小於Low Threshold,當超過指定的範圍,則會產生Interrupt。


ADC Main Functional Description

這小節會對ADC的基本功能,進行更進一步的描述,以及如何使用。

ADC on-off control

  • 啟用ADC : 需設置ADC_CR2 Register中的 ADON bit,如Figure-10和Figure-11。
  • 關閉ADC : 需重置ADC_CR2 Register中的 ADON bit,如Figure-10和Figure-11。
  • 在啟用ADC後,需要等待 ADC power-up time (tSTAB),以STM32F103xC系列,Max- tSTAB = 1 us,如Table-2。
  • ADC轉換過程中,是能夠被停止的,只須重置ADON bit。

ADC Clock

  • ADC Clock是由APB2 clock經RCC controller程式化分頻,如Figure-12。

Channel Selection

  • 一共有16個Channel。
  • 轉換類型分為兩組,Regular & Injected。
  • 任何一組都可以指定轉換的Channel與轉換的順序。

The regular group :

  • 最多可有16個轉換。
  • 對於轉換的Channel與順序需經由ADC_SQRx Registers進行定義,如Figure-14藍框部分。
  • 轉換數量需經由ADC_SQR1中的L[3:0]定義,如Figure-14紅框部分。

The injected group:

  • 最多可有4個轉換。
  • 對於轉換的Channel與順序需經由ADC_JSQR Register進行定義,如Figure-15籃框部分。
  • 轉換數量需經由ADC_JSQR中的L[1:0]定義,如Figure-15紅框部分。

在MCU內部有Temperature sensor,可供使用者使用,如Figure-16。

啟用方式 : 只需將ADC_CR2 register中第23 bit的位置進行設置,即可啟用。


Single conversion mode

  • 進行一次的轉換。
  • CONT bit必須是0。
  • 可透過設置ADC_CR2 register中的ADON bit或是透過外部觸發進行轉換。
  • 在所選的channel轉換完成之後:
    • 當regular channel轉換完成:
      • 資料會存於16-bit ADC_DR register內。
      • EOC flag被硬體設置。
      • 如果EOCIE被設置,則中斷將會產生。
    • 當injected channel轉換完成:
      • 資料會存於16-bit ADC_DRJ1 register內。
      • JEOC flag被硬體設置。
      • 如果JEOCIE被設置,則中斷將會產生。
  • 最後,ADC停止。

Continuous conversion mode

  • 連續的進行轉換。
  • CONT bit必須是1。
  • 可透過設置ADC_CR2 register中的ADON bit或是透過外部觸發進行轉換。
  • 在每次的channel轉換完成之後:
    • 當一個regular channel轉換完成:
      • 資料會存於16-bit ADC_DR register內。
      • EOC flag被硬體設置。
      • 如果EOCIE被設置,則中斷將會產生。
    • 當一個injected channel轉換完成:
      • 資料會存於16-bit ADC_DRJ1 register內。
      • JEOC flag被硬體設置。
      • 如果JEOCIE被設置,則中斷將會產生。

Timing diagram

  • 啟用ADC時,需等待穩定時間tSTAB完畢之後,可開始ADC conversion的動作。(籃框)
  • 在ADC SWSTART / JSWSTART bit設置後,會開始ADC conversion,同時硬體將其bit重置,ADC conversion過程中需等待tCONV。(紅框)
  • 轉換完畢,EOC會被硬體設置,需透過軟體清除,方能下一個Channel轉換。(綠框)

Analog watchdog

  • 當AWD bit被設置,則表示類比電壓以被ADC轉換,並且低於Lower threshold或高於Higher threshold。
  • 這些thresholds是可在ADC_HTR和ADC_LTR的16-bit registers中被程式化。
  • 可設置在ADC_CR1中的AWDIE bit,啟用Interrupt功能。
  • 透過ADC_CR1 register的設置,Analog watchdog能夠使用在單一或多個channels。

Scan mode

  • 這個模式是被用來轉換一群Channel。
  • 透過在ADC_CR1 register中設置SCAN bit,Scan mode 能夠被啟用。
  • 當SCAN bit被設置,ADC會透過在ADC_SQRx registers之中,所設置的regular channels,以及在ADC_JSQRx registers之中,所設置的injected channels,將其所有進行轉換。
  • 轉換方式是一個Group中所有Channel EOC之後,自動地轉換下一組Group中所有的Channel。
  • 當CONT bit被設置,ADC轉換動作會不斷執行的,且不會停止在最後一個Group轉換完畢,而是繼續向第一個Group進行轉換。
  • 當Regular channel在使用Scan mode時,DMA是必須要啟用的,每次EOC資料會存入SRAM中,以免資料遺漏。

Injected channel management

Triggered injection

  • 為了使用Triggered injection,在ADC_CR1 register中JAUTO bit必須是清除得以及SCAN bit必須是設置的。
  • 可透過外部觸發或是對ADC_CR2 register中的ADON bit設置,即可開始轉換Regular channels。
  • 如果在轉換Regular group channels過程中,外部觸發發生了,目前轉換的Channel會被重置,且在Scan once mode中injected channel 會依照順序轉換完畢。
  • 然後,剛剛轉換的Regular group channel轉換程序將會恢復。如果injected group channel在轉換的過程中,使無法被regular group channel 事件Interrupt的。但是,他會在injected group channel轉換完畢後,轉換regular group channel。

Auto-injection

  • 當JAUTO bit被設置,在regular group channels轉換完畢後,injected group channels將會自動的被轉換。
  • 可透過auto-injection的方式,將在ADC_SQRx 和 ADC_JSQR registers中20個的channel依順序的轉換。
  • 在使用這模式時,對於外部觸發injected channels的方式是不可被使用的。
  • 當CONT bit和 JAUTO bit都被設置時,在injected channels轉換完成後,會接續轉換regular channels,不斷循環進行。
  • 當regular 切換成injected channel轉換時,ADC一定會 delay 1 clock,但是當ADC clock prescaler被設置成2時,會有2 ADC clock delay 的週期。

Discontinuous mode

Regular Group

  • 如果要使用此模式,須設置ADC_CR1 register中的DISCEN bit。
  • 他能夠被用來依順序轉換在ADC_SQRx所設定的一部分Channel。
  • 每觸發一次所轉換的Channel數量,需在ADC_CR1中的L[2:0] bits中設定,最多一次8個Channel依順序轉換。

Injected Group

  • 如果要使用此模式,須設置ADC_CR1 register中的JDISCEN bit。
  • 他能夠被用來依順序轉換在ADC_JSQRx所設定的一部分Channel。
  • 每觸發一次所轉換的Channel數量,需在ADC_JCR1中的JL[2:0] bits中設定,最多一次4個Channel依順序轉換。

Calibration

  • ADC有者自我校正模式。
  • 可設置ADC_CR2 register中的CAL bit,啟用Calibration。
  • 一旦校正完畢,CAL bit將會被硬體重置。
  • 建議在ADC啟動後,進行校正動作。
  • 校正完畢後,校正代碼會存在ADC_DR中。

Data alignment (資料對齊)

Data alignment方式有分兩種,一是靠左對齊,另一是靠右對齊,可透過在ADC_CR2中ALIGN bit進行設定。

Channel-by-channel programmable sample time

  • 對於ADC取樣的Cycle數量,可透過ADC_SMPR1和ADC_SMPR2中的SMP[2:0] bits進行修改。
  • 其中每個Channel可設置不同的Sample time。

DMA Request

  • 因為Regular channels所轉換的值是存於獨立的register中,所以需要透過DMA來儲存更多的Regular channel的轉換值。
  • 只有在Regular channel的EOC才會產生DMA request,且使用者能夠設定將ADC_DR register中的值存於指定空間。

Temperature sensor

  • 能夠量測出環境周圍溫度。
  • 溫度感測器是與ADCx_IN16 input channel相互連接,能夠輸出電壓至ADC並轉換成數位邏輯值。
  • 建議Sampling time為17.1 us。
  • 當未使用,能夠將其電源關閉。

Reading the temperature

使用步驟:

  1. 選擇ADCx_IN16 channel。
  2. 將Sample time設為17.1 us。
  3. 在ADC_CR2中設置TSVREFE bit,將Temperature sensor上電。
  4. 可透過ADON bit或外部觸發來轉換ADC。
  5. 讀取在ADC data register中的VSENSE資料。
  6. 使用以下公式來獲得溫度:
    • Temperature = { (V25 – VSENSE) / Avg_Slope } + 25。
Figure-32 Reading the temperature

ADC Interrupts

  • 不管是regular或是injected group都能夠在EOC之後產生中斷。
  • Analog watchdog也能夠產生中斷。

ADC Modes Colletion

ModeDescription
Signal Conversion只轉換一次,且能夠在轉換完成後發生中斷,最後停止。
Continuous Conversion連續轉換,在每一次的Channel轉換,能夠發生中斷。
Scan用來轉換多個Channel。
Discontinuous在每一次的觸發,依指定Channel數量轉換,且能夠產生中斷。
ADC模式種類

以上是關於STM32 ADC的基本介紹。後面文章會以ADC的所有模式進行使用和測試,最後寫出一個針對STM32 的ADC Monitor。

STM32 ADC #1 基礎功能介紹與重點整理〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/what-is-adc1-stm32-stm32f103rct6/feed/ 0 201
Python Exception (Python 異常處理) – 基礎篇(11) – try、except https://jyouknownit.com/python-exception-handling-basics-try-except/?utm_source=rss&utm_medium=rss&utm_campaign=python-exception-handling-basics-try-except https://jyouknownit.com/python-exception-handling-basics-try-except/#respond Wed, 06 Dec 2023 17:43:27 +0000 https://jyouknownit.com/?p=1843 本篇文章中全面解析了 Python 中的異常處理,包括 try-except 基礎、針對特定異常的處理方法,以 […]

Python Exception (Python 異常處理) – 基礎篇(11) – try、except〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
Python Exception (Python 異常處理) - 基礎篇 示意圖

Python Exception (Python 異常處理) – 基礎篇(11)

本篇文章中全面解析了 Python 中的異常處理,包括 try-except 基礎、針對特定異常的處理方法,以及如何靈活運用多個 except、else 和 finally 語句,並通過表格總結了常見異常類型。

Outline

  1. Python 異常處理 – 基本概念
  2. 使用 try 和 except 語句
  3. 處理特定異常
  4. 使用多個 except 語句
  5. 使用 else 和 finally 語句
  6. 參考資料

Python 異常處理 – 基本概念

在討論 Python 的異常處理之前,我們首先需要理解什麼是「異常」(Exception)。在 Python 中,異常是一種在執行期間發生的事件,且中斷了當前正在執行的正常流程,在這個時候Python 提供了一種處理異常 ( 事件 ) 的方式,使得程序可以在發生錯誤時恢復並繼續運行。

什麼是異常(Exception)

異常可以由各種原因觸發,例如:

  • 嘗試除以零(會引發 ZeroDivisionError
  • 引用不存在的變量(會引發 NameError
  • 打開一個不存在的文件(會引發 FileNotFoundError

在 Python 中,所有異常都是從一個名為 Exception 的基類派生出來的。

常見的異常類型

Python 內置了多種異常類型,這些異常類型可以涵蓋大多數常見的錯誤情況。一些常見的異常類型包括:

  • SyntaxError:語法錯誤
  • TypeError:傳入對象類型與要求的不符
  • IndexError:索引超出序列範圍
  • KeyError:查找字典中不存在的鍵
  • ValueError:傳入無效的參數

了解這些常見的異常類型有助於在寫程式時更有效地處理可能發生的錯誤。

異常處理的必要性

在沒有異常處理的情況下,一旦程序遇到錯誤,它將停止執行並顯示錯誤訊息,這可能導致程序的不穩定,甚至崩潰。使用異常處理可以使程序在發生錯誤時仍能繼續運行,並正確的修正錯誤。

在後續文章中,將探討如何在 Python 中使用 try 和 except 語句來處理異常,以及如何針對特定的錯誤進行處理,這將包括具體的範例,以便更好地理解和應用這些概念。

使用 try 和 except 語句

在 Python 中,try 和 except 語句用於處理異常,這是一種基本的錯誤處理機制,能夠幫助程序捕捉並處理在執行過程中可能發生的錯誤,從而避免程序因錯誤而非正常終止。

try 和 except 的基本結構

try 和 except 的使用遵循以下的基本結構:

try:
    # 嘗試執行的代碼
except SomeException:
    # 如果在 try 塊中發生了 SomeException 異常,則執行此塊

在這裡,try 塊中的代碼是你想要嘗試執行的代碼,而 except 塊則定義了當 try 塊中的代碼引發了特定異常時應該執行的代碼。

範例:簡單的錯誤捕捉

讓我們看一個簡單的例子來說明這一機制:

try:
    result = 10 / 0
except ZeroDivisionError:
    print("發生了除以零的錯誤")

在這個例子中,我們嘗試執行一個可能會引發 ZeroDivisionError 的操作(10除以0)。而當這個錯誤發生時,控制流會轉到 except 塊,並打印出錯誤訊息。

處理多種異常

try-except 結構也可以捕捉多種異常類型。這可以通過添加多個 except 塊來實現,每個塊處理一種異常類型:

try:
    # 可能會引發多種異常的代碼
except FirstException:
    # 處理第一種異常
except SecondException:
    # 處理第二種異常

未指定異常的 except 塊

如果 except 塊後面沒有指定異常類型,它將捕捉 try 塊中發生的任何異常

try:
    # 代碼塊
except:
    # 處理任何在 try 塊中發生的異常

雖然這種方法可以捕捉所有異常,但通常不推薦這麼做,因為它會使錯誤處理變得模糊不清,並可能隱藏一些應該被注意到的問題。

良好的異常處理應該具體且清晰,盡可能具體地捕捉異常,並在 except 塊中明確處理這些異常,這樣可以使代碼更健壯,也更容易調試找到問題並實踐正確的解決方式。

處理特定異常

在 Python 中,處理特定異常是一個重要的概念,它允許我們對不同類型的錯誤進行有針對性的反應,當你明確知道某段代碼可能會引發哪些特定的異常時,你可以為這些特定的異常準備相應的處理邏輯。

如何針對特定錯誤寫 except 語句

在 try 塊後面,我們可以跟上一個或多個 except 塊,每個 except 塊都針對一種特定的異常類型。當 try 塊中的代碼觸發異常時,Python 會檢查每個 except 塊並匹配相應的異常類型。

例如,如果你知道某個操作可能會引發 ValueError 或 TypeError,你可以分別為這兩種異常編寫處理邏輯:

try:
    # 可能引發 ValueError 或 TypeError 的代碼
except ValueError:
    # 處理 ValueError
    print("發生了值錯誤")
except TypeError:
    # 處理 TypeError
    print("發生了類型錯誤")

範例:處理特定類型的異常

讓我們來看一個具體的例子。假設我們有一段代碼,該代碼從使用者那裡獲取輸入,並試圖將其轉換為整數:

try:
    num = int(input("請輸入一個數字: "))
except ValueError:
    print("這不是一個有效的數字。請再試一次!")

在上方例子中,如果使用者輸入的不是數字,int() 函數會引發 ValueError,隨後 except 塊就會捕捉到這個異常並處理它。

處理多個異常

有時,你可能會遇到一段代碼可能會引發多種異常的情況。在這種情況下,你可以通過單個 except 語句來捕捉多個異常,方法是將異常類型放在一個括號內,並用逗號分隔:

try:
    # 代碼塊
except (ValueError, TypeError):
    # 同時處理 ValueError 和 TypeError
    print("發生了值錯誤或類型錯誤")

自定義錯誤訊息

在處理異常時,有時需要了解更多關於錯誤的信息。這可以通過在 except 語句中訪問異常對象來實現:

try:
    # 代碼塊
except ValueError as e:
    print(f"發生了值錯誤: {e}")

在這裡,”as e” 語法允許我們訪問與 ValueError 相關的具體錯誤信息。

在Python 異常處理中允許程序對不同類型的錯誤做出更細致和有針對性的反應,並透過精確地捕捉特定異常,你的程序不僅能夠更穩定地運行,而且還能提供更有用的錯誤信息給予使用者。

使用多個 except 語句

在 Python 中,使用多個 except 語句可以讓我們針對不同的異常類型執行不同的錯誤處理代碼,這一種方式來區分處理多種可能在程式執行過程中發生的錯誤。

同時處理多種異常

在一個 try 塊後面,你可以有多個 except 塊,每個 except 塊針對不同的異常。當 try 塊中的代碼發生異常時,Python 會按照它們出現的順序檢查每個 except 塊,並且只執行第一個匹配異常類型的 except 塊。

範例:使用多個 except 塊

考慮以下代碼片段,它嘗試執行幾個可能引發不同錯誤的操作:

try:
    # 這裡的代碼可能引發多種異常
    # 例如:除以零、類型錯誤等
except ZeroDivisionError:
    # 處理除以零的錯誤
    print("不能除以零")
except TypeError:
    # 處理類型錯誤
    print("類型錯誤發生了")
except Exception as e:
    # 捕捉其他所有異常
    print(f"發生了其他錯誤:{e}")

在上方例子中,我們首先捕捉 ZeroDivisionError,然後是 TypeError。如果發生了這些類型的錯誤之一,相關的 except 塊將被執行。如果發生了任何其他類型的異常,最後一個 except 塊(它捕捉所有的 Exception)將被執行。

注意事項

  • 順序重要:except 塊應該從最具體的異常到最通用的異常排列。這是因為一旦匹配到一個 except 塊,後續的 except 塊就不會再被檢查。
  • 避免過度捕捉:雖然使用一個捕捉所有異常的 except 語句(即捕捉 Exception)看起來是一個容易的解決方案,但它可能會隱藏一些不應該被忽視的錯誤。因此,最好是儘可能具體地捕捉異常。

使用多個 except 語句,你可以為你的應用程式提供更靈活且更精確的錯誤處理能力,這不僅可以幫助你的程式更好地應對不同的錯誤情況,還可以提高程式的穩定性可靠性

使用 else 和 finally 語句

在 Python 的錯誤處理機制中,else 和 finally 語句提供了額外的靈活性。這兩個語句可以和 try 及 except 語句一起使用,以處理不同的程序執行情況。

else 語句的作用

else 塊在 try 塊中的代碼成功執行且沒有觸發任何異常時執行。它通常用於執行一些只有在 try 塊成功完成時才需要執行的代碼。

範例:
try:
    # 嘗試執行的代碼,可能會引發異常
except SomeException:
    # 如果發生 SomeException 異常,則執行此塊
else:
    # 如果 try 塊成功,則執行此塊

在這個例子中,如果 try 塊中的代碼成功執行且沒有引發 SomeException,則會執行 else 塊中的代碼。

finally 語句的作用

finally 塊無論是否觸發異常都會執行。這對於進行一些清理工作,如關閉文件或釋放資源等,非常有用。

try:
    # 嘗試執行的代碼,可能會引發異常
except SomeException:
    # 如果發生 SomeException 異常,則執行此塊
finally:
    # 不論是否發生異常,都會執行此塊

無論 try 塊是否成功,或是否捕捉到異常,finally 塊總是會被執行。

整合 else 和 finally

在實際的應用中,else 和 finally 可以與 try 和 except 語句一起使用,形成一個完整的錯誤處理機制。

完整範例:
try:
    # 嘗試執行的代碼
except SomeException:
    # 處理特定異常
except AnotherException:
    # 處理另一種異常
else:
    # 如果沒有異常發生,執行此塊
finally:
    # 無論如何都會執行的代碼

這樣的結構使得程序能夠更全面地處理各種可能的執行情況,並確保重要的清理或結尾工作總是被完成。

使用這些語句可以讓你的錯誤處理邏輯更加精準和全面,並且確保了無論程序執行的情況如何,一些必要的清理和結尾工作都能被正確執行。在實際建立錯誤處理策略時,合理地利用這些工具可以大大提升程序的可靠性。

參考資料

常見的異常,幫各位整理成表格

異常類型描述
Exception所有內置非系統退出異常的基類。
ArithmeticError所有數值計算錯誤的基類。
BufferError與緩衝區相關的操作錯誤。
LookupError當索引或鍵查找失敗時引發的基類。
AssertionError斷言語句(assert)失敗時引發。
AttributeError物件沒有這個屬性時引發。
EOFErrorinput() 遇到文件結束條件(EOF)時引發。
FloatingPointError浮點計算錯誤時引發。
GeneratorExit當生成器(generator)或協程(coroutine)被關閉時引發,不應被捕捉。
ImportError導入模塊/物件失敗時引發。
ModuleNotFoundError找不到模塊時引發,繼承自 ImportError
IndexError索引不在序列的範圍內時引發。
KeyError字典中找不到鍵時引發。
KeyboardInterrupt用戶中斷執行(通常是輸入^C)時引發。
MemoryError操作耗盡記憶體時引發。
NameError找不到本地或全域名稱時引發。
NotImplementedError在用戶定義的基類中,當子類沒有實現基類中的方法時引發。
OSError操作系統不能執行的操作時引發,有多個子類。
OverflowError數值運算超出最大限制時引發。
RecursionError解釋器檢測到過深的遞迴時引發,繼承自 RuntimeError
ReferenceError嘗試訪問已經被垃圾回收機制回收的弱引用時引發。
RuntimeError發生無法歸類到其他類別的錯誤時引發。
StopIteration迭代器沒有更多的值時引發。
SyntaxError語法錯誤時引發。
IndentationError縮排錯誤時引發,繼承自 SyntaxError
TabErrorTab 和空格混用時引發,繼承自 IndentationError
SystemError解釋器系統發生錯誤時引發。
SystemExitPython 解釋器請求退出時引發。
TypeError操作或函數應用於不適當類型的物件時引發。
UnboundLocalError訪問未初始化的本地變數時引發,繼承自 NameError
UnicodeErrorUnicode 相關的錯誤時引發,有多個子類。
常見的異常類型

Python Exception (Python 異常處理) – 基礎篇(11) – try、except〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/python-exception-handling-basics-try-except/feed/ 0 1843
Python 函式 (Python Function) – 基礎篇 – 函式基礎、呼叫、回傳值 https://jyouknownit.com/python-function-basics-python-function/?utm_source=rss&utm_medium=rss&utm_campaign=python-function-basics-python-function https://jyouknownit.com/python-function-basics-python-function/#respond Tue, 05 Dec 2023 08:08:42 +0000 https://jyouknownit.com/?p=1820 本篇文章詳細探討了 Python 函式的基礎概念,從基本定義到進階應用,涵蓋了函式的結構、參數傳遞方式、回傳值處理,以及 Lambda 函式的使用,且文章中還透過實例展示了函式在數據分析、API 交互、算法開發等領域的應用,並透過綜合練習加強讀者的實踐技能,希望本篇能夠幫助初學者和有經驗的開發者深入理解 Python 函式的功能。本篇是一份全面的Python 函式指南,適合所有希望精通 Python 函式的程序猿。

Python 函式 (Python Function) – 基礎篇 – 函式基礎、呼叫、回傳值〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
Python 函式 - 基礎篇 (Python Function) 式意圖

Python 函式 – 基礎篇

Python 函式基本概念 / 函式呼叫 / 函式回傳值

在本篇文章中,詳細探討了 Python 函式的基礎概念,從基本定義到進階應用,涵蓋了函式的結構、參數傳遞方式、回傳值處理,以及 Lambda 函式的使用,且還透過實例展示了函式在數據分析、API 交互、算法開發等領域的應用,並透過綜合練習加強讀者的實踐技能,希望能夠幫助初學者和有經驗的開發者深入理解 Python 函式的功能。本篇算是一份全面的Python 函式指南,適合所有希望精通 Python 函式的程序猿。

Outline

  1. Python 函式基本概念
  2. Python 函式定義和使用函式
  3. 深入Python 函式呼叫
  4. Python 函式回傳值的概念和應用
  5. Python 函式的實際應用
  6. 綜合練習
  7. 參考資料

Python 函式基本概念

定義Python 函式

Python 函式是一段組織好的、可重復使用的代碼,用於執行一個或多個相關的操作。它可以接受輸入參數並返回一個結果。在 Python 中,函式是用 def 關鍵字來定義的。

函式的主要優點是提高代碼的重用性、使代碼更加有組織,並提高可讀性。

Python 函式的結構

一個基本的 Python 函式結構包括以下部分:

  • 函式名:標識函式的唯一名稱。
  • 參數(可選):函式可以接受的輸入值。
  • 函式體:執行特定任務的代碼塊。
  • 返回值(可選):函式執行後返回的結果。

以下是一個簡單的函式範例:

def greet(name):
    return f"Hello, {name}!"

在這個範例中,greet 是函式名,name 是一個參數,函式體包含了一行代碼,返回一個格式化的字符串。

調用Pythono 函式

定義函式後,我們可以通過其名稱來調用它,並傳入相應的參數(如果有的話)。例如:

print(greet("Alice"))

當調用 greet 函式並傳入 “Alice” 作為參數時,它會返回字符串 “Hello, Alice!”。

透過將代碼封裝到函式中,我們可以提高代碼的可重用性和可讀性,並使我們的程序更加模塊化。

在接下來的章節中,將進一步探索函式的不同用法和高級特性,包括參數類型、回傳值以及如何在實際應用中有效地使用函式。

Python 函式定義和使用函式

如何定義Python 函式

函式是用 def 關鍵字在 Python 中定義的。一個函式的基本結構包含函式名、參數列表和函式體。這裡有一個定義函式的範例:

def add_numbers(a, b):
    return a + b

在這個例子中,add_numbers 是函式名,ab 是參數,return a + b 是函式體,它返回兩個參數的和。

參數

參數是傳遞給函式的值。它們是在函式呼叫時指定的,並在函式內部用於執行操作或計算。參數是可選的;一個函式可以沒有參數。

返回值

return 語句用於從函式返回一個值。如果函式不需要返回值,return 語句可以省略。在前面的例子中,return a + b 語句返回了兩個參數的和。

如何使用Python 函式

定義函式後,可以通過函式名來調用它,並傳入所需的參數。例如:

result = add_numbers(5, 3)
print(result)  # 輸出將會是 8

這個調用將 53 作為參數傳遞給 add_numbers 函式,然後打印返回的結果。

範例:計算平均值

讓我們定義一個函式來計算一系列數字的平均值:

def calculate_average(numbers):
    sum_of_numbers = sum(numbers)
    count_of_numbers = len(numbers)
    average = sum_of_numbers / count_of_numbers
    return average

# 使用函式
nums = [10, 20, 30, 40, 50]
avg = calculate_average(nums)
print(f"The average is: {avg}")

這個函式接受一個數字列表作為輸入,計算並返回它們的平均值。

深入Python 函式呼叫

傳遞參數

當呼叫函式時,你可以以不同的方式傳遞參數:

1. 位置參數:這是最常見的方法,其中參數按順序傳遞。

def subtract(a, b):
    return a - b

result = subtract(10, 5)  # a = 10, b = 5

2. 關鍵字參數:允許你通過名稱而不是位置來指定參數,增加了代碼的可讀性。

result = subtract(a=10, b=5)  # 明確指定 a 和 b 的值

3. 預設參數值:定義函式時,可以為參數指定預設值。

def greet(name, message="Hello"):
    return f"{message}, {name}!"

greeting = greet("Alice")  # message 使用預設值 "Hello"

可變長度參數

在某些情況下,你可能希望函式能處理不定數量的參數:

1. 星號(*):用於收集多餘的位置參數。

def sum_all(*args):
    return sum(args)

total = sum_all(1, 2, 3, 4)  # args 會是一個包含所有參數的元組

3. 雙星號()**:用於收集關鍵字參數。

def config(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

config(database="MySQL", user="root", password="123456")

函式作為參數

Python 中的函式可以作為參數傳遞給其他函式。這提供了極大的靈活性。

def apply_function(func, value):
    return func(value)

def double(x):
    return x * 2

result = apply_function(double, 5)  # result 為 10

匿名函式(Lambda 函式)

Lambda 函式提供了一種快速定義單行函式的方法。

multiply = lambda x, y: x * y
print(multiply(2, 3))  # 輸出 6

透過瞭解這些不同的函式呼叫方法和特性,能夠更靈活地使用 Python 來編寫清晰、高效且易於維護的代碼。在後續章節中,我們將探討更高級的函式特性,如遞迴、高階函式和閉包。

Python 函式回傳值的概念和應用

什麼是回傳值?

Python 函式的回傳值是函式執行後返回給呼叫者的數據。它是函式處理過的結果,可以是任何數據類型:數字、字符串、列表、元組,甚至是其他函式或物件。

使用 return 語句

在 Python 中,return 語句用於指定函式的回傳值。當函式執行到 return 語句時,函式會結束,並將 return 後的值返回給呼叫者。

def get_max(a, b):
    if a > b:
        return a
    else:
        return b

max_value = get_max(10, 20)
print(max_value)  # 輸出 20

在這個例子中,get_max 函式返回兩個參數中的最大值。

沒有回傳值

如果函式沒有 return 語句,或者 return 後面沒有跟隨任何值,則函式返回 None。

def print_message(message):
    print(message)

result = print_message("Hello, Python!")
print(result)  # 輸出 None

多個回傳值

Python 允許函式返回多個值,這些值通常以元組的形式返回。

def get_stats(numbers):
    return max(numbers), min(numbers), sum(numbers) / len(numbers)

max_val, min_val, average = get_stats([1, 2, 3, 4, 5])
print(f"Max: {max_val}, Min: {min_val}, Average: {average}")

Python 函式的實際應用

數據處理

函式在數據處理和分析中扮演著重要角色。例如,我們可以寫一個函式來清理或轉換數據:

def clean_data(data):
    # 假設 data 是一個字典列表
    cleaned_data = []
    for item in data:
        cleaned_item = {k: v.strip() if isinstance(v, str) else v for k, v in item.items()}
        cleaned_data.append(cleaned_item)
    return cleaned_data

# 使用函式
raw_data = [{'name': ' Alice ', 'age': 30}, {'name': ' Bob ', 'age': 25}]
processed_data = clean_data(raw_data)
print(processed_data)

在這個例子中,clean_data 函式去除數據中的領先和尾隨空格。

API 交互

函式可以用於處理與外部 API 的交互,如從網絡服務獲取數據或發送數據:

import requests

def fetch_weather_data(city):
    url = f"https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q={city}"
    response = requests.get(url)
    return response.json()

# 使用函式
weather_data = fetch_weather_data('Taipei')
print(weather_data)

這個函式 fetch_weather_data 向天氣 API 發送請求並返回結果。

算法實現

函式是實現和測試算法的理想方式。例如,我們可以用一個函式來實現排序算法:

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

# 使用函式
unsorted_array = [64, 34, 25, 12, 22, 11, 90]
sorted_array = bubble_sort(unsorted_array)
print(sorted_array)

GUI 應用

在圖形用戶界面(GUI)應用程序中,函式可用於響應用戶交互,例如按鈕點擊:

import tkinter as tk

def on_button_click():
    label.config(text="Hello, Python!")

app = tk.Tk()
button = tk.Button(app, text="Click me", command=on_button_click)
button.pack()
label = tk.Label(app)
label.pack()
app.mainloop()

在這個範例中,當按鈕被點擊時,on_button_click 函式會被呼叫。

Python 函式的應用範圍非常廣泛,從數據處理和 API 交互到算法實現和 GUI 開發。

綜合練習

練習一:數據分析函式

寫一個函式 analyze_data,接受一個數字列表作為參數,返回這個列表的最大值、最小值和平均值。

def analyze_data(numbers):
    max_val = max(numbers)
    min_val = min(numbers)
    average = sum(numbers) / len(numbers)
    return max_val, min_val, average

# 測試函式
data = [10, 20, 30, 40, 50]
max_val, min_val, avg = analyze_data(data)
print(f"Max: {max_val}, Min: {min_val}, Average: {avg}")

練習二:字串處理函式

建立一個函式 format_string,它接受一個字串和一個整數 width。這個函式應該返回一個新的字串,長度為 width,原字串居中,兩側用 ‘-‘ 填充。

def format_string(string, width):
    return string.center(width, '-')

# 測試函式
formatted_string = format_string("Hello", 20)
print(formatted_string)  # 輸出: "-------Hello--------"

練習三:函式作為參數

寫一個函式 process_data,它接受一個數字列表和一個函式作為參數。這個函式應該應用傳入的函式到列表的每一個元素上,並返回結果列表。

def process_data(numbers, func):
    return [func(num) for num in numbers]

# 測試函式
data = [1, 2, 3, 4, 5]
processed_data = process_data(data, lambda x: x * x)
print(processed_data)  # 輸出: [1, 4, 9, 16, 25]

練習四:錯誤處理

改進 analyze_data 函式,增加錯誤處理來處理空列表的情況。

def analyze_data(numbers):
    if not numbers:
        return None, None, None
    max_val = max(numbers)
    min_val = min(numbers)
    average = sum(numbers) / len(numbers)
    return max_val, min_val, average

# 測試函式
empty_data = []
max_val, min_val, avg = analyze_data(empty_data)
print(f"Max: {max_val}, Min: {min_val}, Average: {avg}")  # 輸出: Max: None, Min: None, Average: None

以上是Python 函數學習筆記。

參考資料

Python 函式 (Python Function) – 基礎篇 – 函式基礎、呼叫、回傳值〉這篇文章最早發佈於《Jasson's Simple Life》。

]]>
https://jyouknownit.com/python-function-basics-python-function/feed/ 0 1820