動八位數(shù)碼管全解析)
簡介這是一份面向STM32初學者的74HC595驅(qū)動數(shù)碼管源碼示例核心解決單片機IO資源不足時如何通過串轉(zhuǎn)并方式擴展LED顯示端口的問題。工程僅兩個文件一個頭文件與一個C源文件代碼量精簡適合直接閱讀和移植源碼中演示了74HC595移位寄存器工作原理包括串行數(shù)據(jù)寫入、時鐘觸發(fā)、鎖存輸出等關鍵時序并提供了常用段碼表與多位數(shù)碼管動態(tài)掃描方法。文件包僅1KB無多余工程配置可快速集成到Keil或IAR環(huán)境已有526人學習/下載適用于課程設計、電子競賽或個人DIY。通過這份代碼可以直觀理解級聯(lián)能力用少量IO控制多組數(shù)碼管并為后續(xù)擴展矩陣按鍵或點陣屏打下基礎適合配合原理圖快速驗證。1. 三根線驅(qū)動八位數(shù)碼管74HC595把STM32從IO困局里撈出來做多位數(shù)碼管顯示最頭疼的不是顯示邏輯而是IO分配。4位7段數(shù)碼管帶小數(shù)點要32個段引腳哪怕只用共陽/共陰加位選掃描也得吃掉8個段選4個位選共12個IO一旦板上還要掛按鍵、傳感器和串口STM32的GPIO立刻見底。這個LEDS_595方案的思路是把段選全部交給74HC595做串轉(zhuǎn)并MCU只留出數(shù)據(jù)、時鐘、鎖存三根線剩下的引腳全部還給業(yè)務邏輯。595級聯(lián)之后8位甚至16位數(shù)碼管依然只有3個IO顯示刷新由軟件定時器輪詢。這篇文章按實際拆解順序走先把595的移位鎖存時序說清楚再貼出可用的驅(qū)動寫法最后給動態(tài)掃描和級聯(lián)的完整參數(shù)。2. SPI時序與74HC595移位鎖存對齊2.1 先從引腳關系理解74HC595的移位鎖存74HC595內(nèi)部是兩級寄存器移位寄存器和輸出鎖存器。DS引腳的數(shù)據(jù)在SH_CP上升沿被移入移位寄存器連續(xù)移滿8位后ST_CP產(chǎn)生一個上升沿把移位寄存器的內(nèi)容一次性推到Q0~Q7并行輸出。還有兩個控制腳OE是輸出使能低電平有效MR是復位低電平會把移位寄存器清零。實際畫板時OE直接接地、MR接VCC否則浮空會導致偶發(fā)亂碼。引腳功能接法建議DS串行數(shù)據(jù)輸入接STM32的MOSI或任意GPIOSH_CP移位時鐘上升沿采樣接SCLK或GPIO空閑拉低ST_CP鎖存時鐘上升沿輸出必須由MCU單獨控制OE輸出使能低有效直接接GNDMR復位低有效接VCC不可懸空Q0~Q7并行輸出經(jīng)限流電阻接數(shù)碼管段引腳時序上最容易被忽略的是ST_CP的控制位置。有人圖省事每發(fā)送一位就把ST_CP拉高一次結果是數(shù)碼管全程亂閃。正確做法是先讓8個數(shù)據(jù)位全部移入最后一刻再觸發(fā)ST_CP鎖存只發(fā)生一次。2.2 硬件SPI與GPIO模擬兩種方式都要會STM32的硬件SPI可以直接對接595但有一個細節(jié)要先確認資源配套的leds_595.c是GPIO模擬版本不依賴SPI外設目的是讓驅(qū)動能平移到任何一款STM32上。我一般先在CubeMX里驗證時序再把關鍵參數(shù)搬到寄存器版本里。CubeMX里打開SPI1設置為Master參數(shù)如下CPOL Low即空閑時SCLK保持低電平CPHA 1 Edge數(shù)據(jù)在第一個沿被采樣數(shù)據(jù)寬度 8 Bits波特率預分頻 /3272MHz主頻下得到2.25MHz的SCLKMSS SoftwareNSS引腳不用這個組合等價于SPI Mode 0剛好匹配74HC595的時序要求SH_CP上升沿采樣DS。發(fā)數(shù)據(jù)用HAL_SPI_Transmit鎖存引腳單獨操作void HC595_SendViaSPI(uint8_t dat) { HAL_SPI_Transmit(hspi1, dat, 1, 10); // 發(fā)送1字節(jié)超時10ms HC595_ST_CP_LOW(); // 鎖存引腳拉低 delay_us(1); HC595_ST_CP_HIGH(); // 上升沿數(shù)據(jù)到Q0~Q7 }注意HAL_SPI_Transmit只負責移出8個bit真正讓數(shù)碼管亮起來的是后面那次ST_CP上升沿。如果把鎖存放進中斷回調(diào)里反復觸發(fā)顯示會閃爍所以鎖存必須放在主流程或定時器中斷的末尾。GPIO模擬版本盯住三個動作拉低SH_CP、給DS賦值、拉高SH_CP。每一位一個循環(huán)#define HC595_DS_GPIO GPIOA #define HC595_DS_PIN GPIO_PIN_7 #define HC595_SH_GPIO GPIOA #define HC595_SH_PIN GPIO_PIN_5 #define HC595_ST_GPIO GPIOA #define HC595_ST_PIN GPIO_PIN_6 void HC595_ShiftByte(uint8_t dat) { for (uint8_t i 0; i 8; i) { HC595_SH_CP_LOW(); // 時鐘拉低 if (dat 0x80) // 高位在前 HC595_DS_HIGH(); else HC595_DS_LOW(); HC595_SH_CP_HIGH(); // 上升沿移入1位 dat 1; } HC595_ST_CP_LOW(); // 8位移完再鎖存 HC595_ST_CP_HIGH(); }參數(shù)說明這里按高位先發(fā)因為數(shù)碼管段碼的高位對應a段還是dp段取決于硬件接線但多數(shù)段碼表按位7dp、位6g設計。移位循環(huán)里dat 1是逐位把最高位送到DS上。SH_CP拉高的動作要大于幾十納秒GPIO翻轉(zhuǎn)速度完全夠用。如果改成低位先發(fā)段碼表要整體反向排列兩個方案選一個并保持一致。3. 段碼表推算與leds_595.c驅(qū)動拆解3.1 段碼表不是背出來的是算出來的數(shù)碼管段碼表最容易出錯。拿共陰數(shù)碼管舉例a段接595的Q0b段接Q1依此類推到g接Q6、dp接Q7那么數(shù)字0點亮a、b、c、d、e、f二進制就是0b0111111也就是0x3F。數(shù)字1只點亮b、c對應0x06。每次換單片機或者換板子段位順序一變表就得重算。共用一張表遇到共陽取反即可字符共陰段碼共陽段碼00x3F0xC010x060xF920x5B0xA430x4F0xB040x660x9950x6D0x9260x7D0x8270x070xF880x7F0x8090x6F0x90注意8的共陽段碼是0x80很多新手會寫成0x00結果數(shù)字8變成全滅。共陽表為什么是反的共陽數(shù)碼管公共端接VCC某段要亮必須給低電平所以共陰表的每一位取反。3.2 leds_595.c核心發(fā)送函數(shù)資源包里的leds_595.h暴露的函數(shù)不多核心就是發(fā)送、鎖存、清屏。典型實現(xiàn)void LEDS_595_WriteSegment(uint8_t seg_data) { HC595_ShiftByte(seg_data); // 移位寄存器接收段碼 HC595_Latch(); // ST_CP上升沿輸出 } void LEDS_595_Clear(void) { HC595_ShiftByte(0x00); // 共陰全滅 HC595_Latch(); }這里發(fā)送和鎖存分成兩個函數(shù)是刻意的。多位數(shù)碼管場景下數(shù)據(jù)更新的順序一般是先關閉所有位選再逐片發(fā)送新段碼最后統(tǒng)一鎖存這樣能避免殘影。如果每片595都單獨鎖存一次位選還亮著中間態(tài)就會被肉眼捕捉到。3.3 靜態(tài)顯示與位選的關系單個數(shù)碼管只靠一片595就能亮但四位以上必須處理位選。位選信號控制當前哪一位數(shù)碼管被通電位選可以掛在595上也可以直接由STM32的GPIO輸出。掛在GPIO上時邏輯最簡單void LEDS_595_DisplayDigit(uint8_t pos, uint8_t num) { uint8_t code segment_table[num]; LEDS_595_Clear(); // 關位選防止殘影 LEDS_595_WriteSegment(code); // 段碼進入595 GPIO_SetBits(SEG_GPIO, 1 pos); // 打開第pos位 }注意Clear和WriteSegment之間不要插入延時。位選打開之前必須先讓段碼在595輸出端穩(wěn)定否則數(shù)碼管會閃出上一個字符的殘像。實際工程中很多人把595掛在GPIO位選后面段碼晚到半個時鐘周期肉眼看到的就是重影。把Clear放在最前面利用595鎖存的瞬間同時切斷所有位再更新數(shù)據(jù)問題就沒了。4. 動態(tài)掃描刷新率與74HC595級聯(lián)擴展4.1 為什么多位數(shù)碼管必須動態(tài)掃描每位數(shù)碼管獨立靜態(tài)顯示需要8個段引腳加1個公共端4位就是36個引腳這在任何單片機上都不可接受。動態(tài)掃描的核心是視覺暫留同一時刻只點亮一位輪流點亮所有位刷新率足夠高時人眼看到的是完整數(shù)字。掃描頻率至少要50Hz才不會閃工程上取100Hz比較穩(wěn)也就是一輪完整的4位掃描耗時10ms每位點亮2.5ms。這個參數(shù)在STM32定時器中斷里實現(xiàn)中斷周期 10ms / 位數(shù)。4位取2.5ms8位取1.25ms。周期太短單段點亮時間不足會顯得暗太長則閃爍。用定時器做時基比在主循環(huán)里delay可靠得多主循環(huán)被串口或按鍵阻塞時顯示不會頓挫。4.2 定時器中斷里的掃描循環(huán)volatile uint8_t scan_pos 0; uint8_t display_buf[4] {1, 2, 3, 4}; // 要顯示的數(shù)字 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); LEDS_595_Clear(); // 關閉所有位選 LEDS_595_WriteSegment(segment_table[display_buf[scan_pos]]); SEG_SelectPin(scan_pos); // 打開當前位 scan_pos; if (scan_pos 4) scan_pos 0; } }邏輯說明每進一次中斷先把上一輪的位選全部關掉再送當前位段碼最后選通當前位。Clear和WriteSegment中間不能有其它IO操作否則595輸出端的毛刺會被位選放大。參數(shù)說明TIM2預分頻和重載值的計算方法是 中斷頻率 72MHz / (PSC 1) / (ARR 1)。要得到2.5ms中斷周期也就是400Hz頻率PSC取71、ARR取2499誤差為零。如果用的是8位數(shù)碼管ARR改成1249即可。中斷里不要做耗時操作段碼查表是常量數(shù)組訪問微秒級完成。4.3 級聯(lián)時先發(fā)最后一級74HC595級聯(lián)后DS接前一級的Q7S。數(shù)據(jù)流從最后一片往前傳先發(fā)送最后一片要顯示的數(shù)據(jù)再發(fā)送第一片的數(shù)據(jù)這樣最先到達的數(shù)據(jù)最后被推到末端芯片。兩片級聯(lián)驅(qū)動8位數(shù)碼管時void LEDS_595_Display8(uint8_t hi_code, uint8_t lo_code) { HC595_ShiftByte(hi_code); // 先送高位片的段碼 HC595_ShiftByte(lo_code); // 再送低位片的段碼 HC595_Latch(); // 所有數(shù)據(jù)就位統(tǒng)一鎖存 }級聯(lián)還有一層好處段選和位選分開各用一片595。第一片輸出段碼第二片輸出位選MCU仍只占用DS、SH_CP、ST_CP三根線。此時位選輸出接三極管基極由三極管驅(qū)動數(shù)碼管的公共端而不是直接把595輸出接數(shù)碼管。595每個腳在3.3V供電時拉電流只有4mA左右直接點亮8段數(shù)碼管會明顯變暗加ULN2003或者S8550擴流是常見做法。5. 邏輯分析儀驗證與常見時序坑5.1 上板前先用邏輯分析儀抓時序把DS、SH_CP、ST_CP三根線接到邏輯分析儀上發(fā)送一個0x3F。正確波形應該是SH_CP產(chǎn)生8個上升沿每個上升沿前DS的電平已經(jīng)穩(wěn)定第8個上升沿之后ST_CP才出現(xiàn)一個上升沿。如果ST_CP和SH_CP同時跳變鎖存時最后一bit還沒移完顯示的就是錯誤段碼。沒有邏輯分析儀時可以用萬用表驗證靜態(tài)輸出。把數(shù)碼管焊下來給595發(fā)送0x01只有Q0輸出高電平。依次發(fā)送0x02、0x04、0x08、0x10、0x20、0x40、0x80每發(fā)一次測一次Q0~Q7的電壓電平應該逐位變化。這一步能同時排除虛焊和引腳順序錯誤。5.2 五個高頻故障點現(xiàn)象原因排查方向全部數(shù)碼管不亮OE懸空或MR被拉低OE接地、MR接VCC數(shù)字亂跳ST_CP在每個bit都觸發(fā)只在8位移完后鎖存有重影位選關閉和段碼更新順序反了先Clear再送段碼最后選通亮度不均掃描周期和位數(shù)不匹配總周期10ms除以位數(shù)級聯(lián)后高低位數(shù)字互換發(fā)送順序反了先送顯示在最右端的字節(jié)最后一類坑是模板工程常見的MR引腳沒接上拉上電瞬間595處于復位狀態(tài)偶發(fā)一次全滅后恢復正常。這屬于硬件復位毛刺軟件上可以在初始化時對HC595_ShiftByte(0x00)再鎖存一次把輸出寄存器刷成已知狀態(tài)。GPIO模擬版本移植到新板子時先確認DS、SH_CP、ST_CP三個引腳都配成推挽輸出不要漏掉GPIO時鐘使能。74HC595的VCC建議就近放一個0.1uF去耦電容級聯(lián)線超過10cm時在末端并一個100uF電解電容不然動態(tài)掃描電流突變會把MCU電壓拉低嚴重的會觸發(fā)看門狗復位。本文還有配套的精品資源點擊獲取