AIR32F103(九) CAN匯流排的通信和ID過濾機制及實例

来源:https://www.cnblogs.com/milton/archive/2023/02/25/17155649.html
-Advertisement-
Play Games

CAN 是一種典型的廣播式網路, 在實際應用中, 如果只希望接收到特定類型的數據, 就要藉助過濾器來實現. AIR32/STM32的CAN控制器包含14個過濾器, 可以設置為 屏蔽模式 或 列表模式 對CAN匯流排上的報文進行過濾. 當節點希望接收到一種報文時, 可以用屏蔽位模式進行過濾, ...


目錄

CAN 匯流排

CAN是 Controller Area Network 的簡稱, 最初由BOSCH公司開發, 後來成為國際標準(ISO 11898), 是當前應用最廣泛的現場匯流排之一, 是汽車控制系統和嵌入式工業控制區域網事實上的標準.

CAN 匯流排的物理連接

相對於近距離傳輸的I2C, SPI協議, 以及RS485匯流排, CAN 匯流排定義了更先進的物理層和鏈路層, 以及種類豐富的上層協議. 與I2C, SPI等基於時鐘信號同步的通訊方式不同, CAN通訊不使用時鐘信號進行同步, 它是一種非同步通訊, 只有 CAN_High 和 CAN_Low 兩條信號線, 共同構成一組差分信號線, 以差分信號的形式進行通訊.

CAN 物理層主要分為閉環匯流排及開環匯流排網路兩種形式, 一個適合於高速通訊, 一個適合於遠距離通訊.

  • CAN閉環通訊網路是一種遵循 ISO11898 標準的高速, 短距離網路, 匯流排最大長度為40m, 通信速度最高為1Mbps, 匯流排的兩端各要求有一個120歐的電阻做阻抗匹配, 減少回波反射.
  • CAN開環匯流排網路是遵循 ISO11519-2 標準的低速, 遠距離網路, 最大傳輸距離為1km, 最高通訊速率為125kbps, 兩根匯流排互相獨立, 不形成閉環, 要求每根匯流排上各串聯有一個 2.2KR 的電阻.

關於共地

CAN 在多個收發器之間的連接, 可以不共地, 只需要 CANH 和 CANL 兩線連接.

CAN 匯流排的通信機制

CAN匯流排上可以掛載多個通訊節點, 節點之間的信號經過匯流排傳輸, 實現節點間通訊. CAN通訊協議不對節點進行地址編碼, 而是對數據內容進行編碼, 所以網路中的節點個數理論上不受限制, 只要匯流排的負載足夠即可, 可以通過中繼器增強負載.

CAN通訊節點由一個CAN控制器及CAN收發器組成, 控制器與收發器之間通過 CAN_Tx及 CAN_Rx 信號線相連, 收發器與CAN匯流排之間使用 CAN_High 及 CAN_Low 信號線相連. 其中 CAN_Tx 及 CAN_Rx 使用普通的類似TTL邏輯信號, 而 CAN_High 及 CAN_Low 是一對差分信號線, 當CAN節點需要發送數據時, 控制器把要發送的二進位編碼通過 CAN_Tx 線發送到收發器, 然後由收發器把這個普通的邏輯電平信號轉化成差分信號, 通過差分線 CAN_High 和 CAN_Low 輸出到CAN匯流排網路. 收發器接收匯流排上的數據時則是相反的過程, 收發器把匯流排上收到的 CAN_High 及 CAN_Low 信號轉化成普通的邏輯電平信號, 再通過 CAN_Rx 輸出到控制器中.

差分信號

差分信號又稱差模信號, 與傳統使用單根信號線電壓表示邏輯的方式有區別, 使用差分信號傳輸時, 需要兩根信號線, 這兩個信號線的振幅相等, 相位相反, 通過兩根信號線的電壓差值來表示邏輯0和邏輯1. 相對於單信號線傳輸的方式, 使用差分信號傳輸具有如下優點

  • 抗干擾能力強, 當外界存在雜訊干擾時, 幾乎會同時耦合到兩條信號線上, 而差分信號只關心兩個信號的差值, 所以外界的共模雜訊可以被抑制
  • 能有效抑制它對外部的電磁干擾, 由於兩根信號的極性相反, 對外輻射的電磁場可以相互抵消, 耦合的越緊密, 泄放到外界的電磁能量越少
  • 時序定位精確, 由於差分信號的開關變化是位於兩個信號的交點, 而不像普通單端信號依靠高低兩個閾值電壓判斷, 因而受工藝, 溫度的影響小, 能降低時序上的誤差, 同時也更適合於低幅度信號的電路

由於差分信號的這些優點, 在USB協議, 485協議, 乙太網協議及CAN協議的物理層中, 都使用了差分信號傳輸.

CAN協議中的差分信號

CAN協議中對它使用的 CAN_High 及 CAN_Low 表示的差分信號做了規定. 以高速CAN協議為例, 當表示邏輯1時(隱性電平), CAN_High 和 CAN_Low 線上的電壓均為2.5V, 即它們的電壓差為 0, 而表示邏輯0時(顯性電平), CAN_High的電平為 3.5V, CAN_Low線的電平為1.5V, 電壓差為 2V.

CAN 匯流排的特點

CAN 匯流排網路是一種多主網路, 在匯流排處於空閑狀態時, 任何一個節點單元都可以申請成為主機, 向匯流排發送消息. 其原則是: 最先訪問匯流排的節點單元可以獲得匯流排的控制權, 多個節點單元同時嘗試獲取匯流排的控制權時, 將發生仲裁事件, 具有高優先順序的節點單元將獲得匯流排控制權.

CAN 協議中, 所有的消息都以固定的數據格式打包發送. 兩個以上的節點單元同時發送信息時, 根據節點標識符(常稱為 ID, 打包在固定的數據格式中)決定各自優先順序關係, CAN 匯流排沒有其他匯流排的地址概念, 在匯流排上增加節點單元時, 連接在匯流排的其他節點單元的軟硬體都不需要改變.

CAN 匯流排的通信速率和匯流排長度有關, 在匯流排長度小於 40m 的場合中, 數據傳輸速率可以達到 1Mbps, 而即便匯流排長度上升至 1000m, 數據的傳輸速率仍可達到 50Kbps, 無論在速率還是傳輸距離都明顯優於常見的 RS232, RS485 和 I2C 匯流排.

對於匯流排錯誤, CAN 匯流排有錯誤檢測功能, 錯誤通知功能, 錯誤恢復功能三種應對措施, CAN 匯流排上的每個節點都可以通過判斷得出, 當前匯流排上的錯誤是暫時錯誤(如瞬間的強干擾)還是持續錯誤(如匯流排斷裂). 當匯流排上發生持續錯誤時, 引起故障的節點單元會自動脫離匯流排.

CAN 匯流排上的節點數量在理論上沒有上限, 但在實際上收到匯流排上的時間延時及電氣負載的限制. 降低最大通信速率可以增加節點單元的連接數, 反之減少節點單元的連接數則最大通信速率可以提高.

CAN 匯流排通信

CAN 數據位傳輸時間

為了實現位同步, CAN協議把每一個數據位(bit)的時序分解成SS段, PTS段, PBS1段, PBS2段, 這四段的長度加起來即為一個CAN數據位的長度.

CAN傳輸的最小的時間單位是Tq(即CAN外設的時鐘周期), 一個完整的位由8~25個Tq組成.

CAN 的數據幀

CAN 匯流排的數據通信是以數據幀的格式進行的, 瞭解CAN的數據幀, 可以幫助瞭解CAN的過濾機制. 下麵是一個完整的CAN數據幀的結構

CAN Data Frame

  • SOF 應該是 start of frame, 一個bit位拉低匯流排用於開始傳輸
  • Identifier 就是數據的 ID, 11個bit
  • RTR (Remote Transmission Request) 用於聲明這是一個數據幀還是一個遠程幀
  • IDE 用於聲明使用的標準ID還是擴展ID
  • r 是一個保留 bit
  • DLC 用於聲明後面攜帶的數據位元組數量
  • Data Field 包含的是發送的數據, 最多不超過8個位元組
  • Checksum 和 DEL 是校驗碼和對應的分隔符
  • ACK 和 DEL 是響應位和對應的分隔符

CAN 數據幀對應的代碼

用C語言描述的CAN幀整體結構為

CAN_TxHeaderTypeDef   TxHeader;
uint8_t               TxData[8];
uint32_t              TxMailbox;
  • TxHeader 用於存儲頭信息, 包含了 RTR, DLC 等, 在SPL中對應的類型為 CAN_TxHeaderTypeDef
  • TxData 用於存儲傳輸的數據
  • TxMailbox 用於發送此消息的 mailbox

幀頭的結構和幀數據

TxHeader.StdId = 0x446;
TxHeader.RTR = CAN_RTR_DATA;  // Remote or data frame
TxHeader.IDE = CAN_ID_STD;    // Standard or extended
// reserved bit
TxHeader.DLC = 2;             // Data length in bytes
  • CAN_ID_STD 表示使用了標準ID模式(非擴展ID)
  • 0x446 就是發送的ID, 位寬最大為 11-bit
  • CAN_RTR_DATA 表示這個幀為數據幀
  • DLC 標識後面數據的位元組長度, 因為發送兩個位元組, 所以這裡是2
  • 隨後在 TxData 中存儲兩個位元組
TxData[0] = 50;  
TxData[1] = 0xAA;

AIR32F103 / STM32 的 CAN 外設

以下的描述適用於AIR32F103和STM32.

bxCAN 控制器 (Basic Extended CAN) 支持CAN協議2.0A和2.0B標準. 該CAN控制器支持最高的通訊速率為1Mbps, 可以自動地接收和發送CAN報文, 支持使用標準ID和擴展ID的報文. 外設中具有3個發送郵箱, 發送報文的優先順序可以使用軟體控制, 還可以記錄發送的時間;具有2個3級深度的接收FIFO, 可使用過濾功能只接收或不接收某些ID號的報文; 可配置成自動重發; 不支持使用DMA進行數據收發.

CAN波特率的計算

通過配置位時序寄存器CAN_BTR的TS1[3:0]及TS2[2:0]寄存器位設定BS1及BS2段的長度後, 可以確定每個CAN數據位的時間

BS1段時間

Tbs1 =Tq x (TS1[3:0] + 1)

BS2段時間

Tbs2 = Tq x (TS2[2:0] + 1)

整個數據位的時間

Tbit = 1Tq + Tbs1 +Tbs2 = 1 + (TS1[3:0] + 1)+ (TS2[2:0] + 1)

Tq 是 CAN 通信的最小時間單元, 與 CAN 時鐘匯流排及分頻器配置有關, CAN1和CAN2外設都是掛載在APB1匯流排上的, 而位時序寄存器 CAN_BTR 中的 BRP[9:0] 寄存器位可以設置CAN外設時鐘的分頻值 , 所以

Tq = brp * Tpclk = (BRP[9:0]+1) * Tpclk

其中的PCLK指APB1時鐘, 預設值為36MHz. 可以計算出 CAN 的波特率:

BaudRate = 1 / Tbit = Fpclk / ((Tbs1 + Tbs2 + 1) * brp)

CAN 波特率的設置

CAN_InitStructure.CAN_TTCM = DISABLE; // time triggered communication mode off
CAN_InitStructure.CAN_ABOM = DISABLE; // automatic bus-off management off
CAN_InitStructure.CAN_AWUM = DISABLE; // automatic wake-up mode off, wakeup by software cleaar CAN->MCR SLEEP bit
CAN_InitStructure.CAN_NART = ENABLE;  // no-automatic retransmission mode on
CAN_InitStructure.CAN_RFLM = DISABLE; // rx FIFO Locked mode off
CAN_InitStructure.CAN_TXFP = DISABLE; // transmit FIFO priority off
CAN_InitStructure.CAN_Mode = mode;
// Set baud rate
CAN_InitStructure.CAN_SJW = tsjw;      // synchronisation_jump_width, CAN_SJW_1tq ~ CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = tbs1;      // number of time quanta in Bit Segment 1, CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_BS2 = tbs2;      // number of time quanta in Bit Segment 2, CAN_BS1_1tq ~ CAN_BS1_16tq
CAN_InitStructure.CAN_Prescaler = brp; // clock prescaler, 1~1024
CAN_Init(CAN1, &CAN_InitStructure);

CAN 的ID過濾機制

CAN 是一種典型的廣播式網路, 在實際應用中, 如果只希望接收到特定類型的數據, 就要藉助過濾器來實現. AIR32/STM32的CAN控制器包含14個過濾器, 可以設置為 屏蔽模式列表模式 對CAN匯流排上的報文進行過濾. 當節點希望接收到一種報文時, 可以用屏蔽位模式進行過濾, 當節點希望接受到單一類型報文時, 應該配置為列表模式.

CAN控制器的每個過濾器都具備一個寄存器, 稱為屏蔽寄存器。其中標識符寄存器的每一位都有屏蔽寄存器的每一位所對應.

AIR32/STM32 使用 CAN 外設內建的過濾器, 初始化代碼為

  CAN_FilterTypeDef canfilterconfig;

  canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
  canfilterconfig.FilterBank = 18;  // 指定使用哪個過濾器
  canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  canfilterconfig.FilterIdHigh = 0x103<<5;
  canfilterconfig.FilterIdLow = 0;
  canfilterconfig.FilterMaskIdHigh = 0x103<<5;
  canfilterconfig.FilterMaskIdLow = 0x0000;
  canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
  canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
  canfilterconfig.SlaveStartFilterBank = 20;  // how many filters to assign to the CAN1 (master can)

  HAL_CAN_ConfigFilter(&hcan1, &canfilterconfig);

FilterMode 用於設置過濾模式, 在STM32中有兩種過濾模式, 這裡使用的是掩碼模式

  • MASK MODE, 掩碼模式, 使用寄存器中設置的掩碼對接收到的ID中特定的位進行比較.
  • LIST MODE, 列表模式, 對於接收到的ID, 直接使用寄存器中的ID進行比較.

FilterScale 用於指定是 1)一個32bit的過濾寄存器, 還是 2)兩個16bit的過濾寄存器. 這裡使用的是一個 32 Bit 寄存器.

FilterIdHigh 用於設置 ID 寄存器的高16 Bits, 這裡的值會被用於與輸入的ID進行比較. 這裡只比較接收到的消息的標準ID, 因此將值左移5位, STD ID 從 ID HIGH Register 的第5位開始.

FilterMaskIdHigh 是掩碼寄存器的高16 Bits, 在對接收到的消息的ID進行比較時, 會忽略這個寄存器中bit=0的位, 僅對會對bit=1對應的位, 與ID寄存器中對應的位進行比較.

掩碼模式過濾的圖例說明

上圖中, CAN_FxR1 和 CAN_FxR2 都是32bit寄存器, 用於存儲過濾器的 ID 和 Mask 設置, 紅色框和綠色框分別對應代碼中的 FilterIdHigh + FilterIdLow 和 FilterMaskIdHigh + FilterMaskIdLow.

  1. 這個例子中僅使用標準ID, 不使用擴展ID時, 低5位可以忽略
  2. 設置ID和Mask時, 都要將需要設置的ID值(0x103)左移5位, 因為低5位用於extId
  3. 與接收的ID對比時, 會根據Mask中bit值為1的位, 與ID中的對應bit進行比較, Mask中bit為0的位都會忽略. 這個例子中需要比較的位是0, 1 和 8
  4. 接收到的ID, 與ID寄存器中對應bit的值進行比較, 當這幾個bit的值都一致時, ID就是匹配的, 消息就會被接收, 否則會被過濾

根據上面的設置

  • 如果輸入的是0x102, 根據Mask設置, 第0,1,8位會用於比較, 而ID的第0位為1, 所以這個輸入會被忽略
  • 如果輸入的是0x107, 根據Mask設置, 第0,1,8位會用於比較, ID設置的這三位都是1和輸入的一致, 所以這個輸入會被接收

帶擴展ID的例子

根據手冊 standard frames with 11-bit identifiers as well as extended frames with 29-bit identifiers, 擴展幀除了原有的 11 bits 標準ID外, 還帶 18 bits 的擴展ID. 為什麼是29 bits? 因為後面還有3個bit的功能標誌位

如果

  • 將 stdId 和 extId 合併為一個29位的 filterId, 其結構為 EXTID[17:0] | STDID[10:0] (從高bit位到低bit位)
  • 將對應的 mask 也合併為一個29位的 filterMask, 其結構為 EXTMASK[17:0] | STDMASK[10:0]

過濾條件的設置代碼為

// 取值 STID[10:0] & EXTID[17:13], 因為 CAN_FilterIdHigh 是 16bit, 所以 filterId << 5 的高16bit會被忽略.
filter.CAN_FilterIdHigh = ((filterId << 5)  | (filterId >> (32 - 5))) & 0xFFFF;
// 取值 EXID[12:5] & 3 Reserved bits, 這裡同樣, filterId 移位後的高16bit會被忽略
filter.CAN_FilterIdLow = (filterId >> (11 - 3)) & 0xFFF8;
// 與上面同理
filter.CAN_FilterMaskIdHigh = ((filterMask << 5)  | (filterMask >> (32 - 5))) & 0xFFFF;
filter.CAN_FilterMaskIdLow = (filterMask >> (11 - 3)) & 0xFFF8;

測試示例

硬體準備

  • TJA1050 或 MCP2551 的 CAN 收發模塊 x 2, 可以混合使用, TJA1050 與 MCP2551 可以互相通信
  • 帶 AIR32F103 的開發板

因為 TJA1050 和 MCP2551 都是5V供電, 因此開發板上要有5V輸出, 否則需要單獨供電

示例代碼

代碼倉庫目錄 https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/CAN

這個目錄下包含兩個模式的例子, 一個是 Loopback, 一個是 Normal, 從合宙官方倉庫的例子參考(抄)的.

Loopback 模式

Loopback 是測試模式, 發送的數據不進入匯流排直接進入接收隊列, 用於檢查 MCU 與 CAN 收發器之間通信是否正常, 以及 CAN 收發器是否正常工作. 運行後在串口輸入's', 會發送8個位元組並將接收到的數據通過串口回顯.

Normal 模式

正常的通信模式, 需要兩套 MCU + CAN 收發器. CAN 收發器之間通過 CANH 和 CANL 連接. 代碼中設置過濾器時, 使用的是相同的 ID 和 Mask 值, 對兩個MCU編譯燒錄時需要將 ID_TARGET 和 ID_RECEIV 的值互換一下.

運行後, 在一側串口輸入's', 在另一側會通過串口顯示接收到的數據.

問題

在測試中, 一開始給 TJA1050 錯誤使用了 3.3V 電壓, 在 Loopback 模式工作正常, 但是在 Normal 模式工作不正常, 只有將兩邊收發器共地才能正常通信, 如果換成 MCP2551 則完全不能通信.

這些問題在將電壓換成 5V 後就正常了.

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • OpenAI註冊(使用代理vpn才行) 準備: 外國郵箱,如gmail、outlook、iCloud郵箱等 外國手機號,或者能接受外國手機驗證碼既可以 (沒有的話文章里有推薦) 1.註冊我們的OpenAI賬號, 訪問地址:https://platform.openai.com/signup 點擊si ...
  • 分散式存儲系統將用戶存儲的數據根據某種規則存儲到不同機器上,當用戶想要獲取指定數據時,再按照規則到存儲數據的機器中獲取。數據生產者/消費者、數據索引和數據存儲是分散式存儲系統的三大要素。 ...
  • 教程簡介 MicroStrategy初學者教程 - 從簡單和簡單的步驟學習MicroStrategy,從基本到高級概念,包括概述,環境設置,桌面,架構,組件概述,導入數據,OLAP服務,配置對象,公共對象,模式對象,報告對象,報表類型,切片,切塊,透視,鑽取和向下鑽取,彙總,創建度量標準,嵌套度量標 ...
  • QT連接MySQL失敗(本人QT版本:5.12.5) 一、報錯結果 QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: available drivers: QSQLITE QODBC QODBC3 QPSQL QPSQL7 二、報錯原因 缺少M ...
  • 簡介 volatile是Java提供的一種輕量級的同步機制。Java 語言包含兩種內在的同步機制:同步塊(或方法)和 volatile 變數,相比於synchronized(synchronized通常稱為重量級鎖),volatile更輕量級,因為它不會引起線程上下文的切換和調度。但是volatil ...
  • PHP使用ZipArchive批量打包壓縮文件,並下載。使用php自帶的ZipArchive類,可以壓縮或解壓文件。 首先需要確定已經安裝了zip擴展,如果沒有安裝,請先安裝,下載:http://pecl.php.net/package/zip (相應php版本的zip包) 先把需要下載的文件路徑找 ...
  • .NET Core 依賴註入的基本用法 話接上篇,這一章介紹 .NET Core 框架自帶的輕量級 Ioc 容器下服務使用的一些知識點,大家可以先看看上一篇文章 [ASP.NET Core - 依賴註入(一)] 2.3 服務解析 通過 IServiceCollection 註冊了服務之後,可以通過以 ...
  • RestSharp RestSharp是一個輕量的,不依賴任何第三方的模擬Http的組件或者類庫。RestSharp具體以下特性;支持net4.0++,支持HTTP的GET, POST, PUT, HEAD, OPTIONS, DELETE等操作,支持oAuth 1, oAuth 2, Basic, ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...