修改前的工程:https://zh.arslanbar.net/post.php?t=24586
【最終效果】
打開網頁時,自動把數字加1,並顯示數碼管上顯示的數字。
例如訪問網頁「http://enc28j60/wel」時,顯示的內容如下:
請求的網頁文件名稱是: /wel
num=170
同時,數碼管動態掃描顯示數字170。
目前共有11篇帖子。
![]() |
修改前的工程:https://zh.arslanbar.net/post.php?t=24586
【最終效果】 打開網頁時,自動把數字加1,並顯示數碼管上顯示的數字。 例如訪問網頁「http://enc28j60/wel」時,顯示的內容如下: 請求的網頁文件名稱是: /wel num=170 同時,數碼管動態掃描顯示數字170。 |
![]() |
首先打開main.c文件,添加全局變量:
uint8_t num = 0; const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; 在main函數中的lwip_init函數前,添加如下代碼: // 配置數碼管掃描 RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 數碼管掃描埠 (74HC595驅動) GPIOB->CRH = 0x00000033; GPIOB->CRL = 0x30000000; RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; // 用定時器7來定時掃描 TIM7->ARR = 49; // 掃描頻率: 50*0.1ms=5ms TIM7->PSC = 7199; // 0.1ms基準 TIM7->DIER = TIM_DIER_UIE; NVIC_EnableIRQ(TIM7_IRQn); // 開中斷 TIM7->EGR = TIM_EGR_UG; // 觸發中斷, 立即點亮數碼管 TIM7->CR1 = TIM_CR1_CEN; // 開定時器 |
![]() |
在main函數下添加定時器7的中斷服務函數: 該函數每次只點亮一個數碼管,從低位掃描到高位。 |
![]() |
本程序的關鍵是解決數碼管掃描中斷打斷lwip和網卡驅動原本的程序而卡死的問題。有兩種解決方法: 顯然方法2要更好一些,畢竟關中斷的時間要比方法1短得多。數碼管的掃描也就不容易出現閃爍。 |
![]() |
採用方法2後,就算在瀏覽器里一直按著F5刷新鍵不放,程序也不會卡死,並且數碼管絲毫沒有閃爍。
|
![]() |
【修改後的4個網卡驅動函數】
// 讀緩衝區 void ENC28J60_ReadBuf(uint8_t *p, uint16_t len) { uint8_t data; uint8_t first = 1; if (len == 0) return; __disable_irq(); SPI1->CR1 |= SPI_CR1_SPE; SPI1->DR = ENC28J60_READ_BUF_MEM; // 通過SPI發送讀取緩衝區命令 while ((SPI1->SR & SPI_SR_TXE) == 0); // 循環讀取 while (len--) { SPI1->DR = 0xff; // 送入下一次要發送的數據 while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待本次數據發送完畢 data = SPI1->DR; // 接收本次數據 if (first) first = 0; // 忽略第一次數據 else *p++ = data; while ((SPI1->SR & SPI_SR_TXE) == 0); } while ((SPI1->SR & SPI_SR_RXNE) == 0); *p = SPI1->DR; // 接收最後一次數據 while (SPI1->SR & SPI_SR_BSY); SPI1->CR1 &= ~SPI_CR1_SPE; __enable_irq(); } uint8_t ENC28J60_ReadOP(uint8_t op, uint8_t addr) { uint8_t data; __disable_irq(); SPI1->CR1 |= SPI_CR1_SPE; // 啟動SPI總線, 同時自動拉低片選信號CS SPI1->DR = op | (addr & 0x1f); // 操作碼和地址 (1) while ((SPI1->SR & SPI_SR_TXE) == 0); // TXE置位時數據(1)剛好發完1位, 可以向DR中放入下次要發送的數據 SPI1->DR = 0xff; // 送入數據 (2) while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待數據(1)發送完畢 data = SPI1->DR; // 接收 (1) while ((SPI1->SR & SPI_SR_TXE) == 0); // TXE置位時數據(2)剛好發完1位 if (addr & 0x80) // 如果是MAC和MII寄存器, 第一個讀取的字節無效, 該信息包含在地址的最高位 SPI1->DR = 0xff; // 送入數據 (3), 再次通過SPI讀取數據 while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待數據(2)發送完畢 data = SPI1->DR; // 接收 (2) if (addr & 0x80) { while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待數據(3)發送完畢 data = SPI1->DR; // 接收 (3) } while (SPI1->SR & SPI_SR_BSY); SPI1->CR1 &= ~SPI_CR1_SPE; // 關閉SPI總線, 同時自動拉高片選信號CS __enable_irq(); return data; } // 寫緩衝區 void ENC28J60_WriteBuf(uint8_t *p, uint16_t len) { __disable_irq(); SPI1->CR1 |= SPI_CR1_SPE; SPI1->DR = ENC28J60_WRITE_BUF_MEM; // 發送寫取緩衝區命令 while ((SPI1->SR & SPI_SR_TXE) == 0); // 循環發送 while (len--) { SPI1->DR = *p++; while ((SPI1->SR & SPI_SR_TXE) == 0); } while (SPI1->SR & SPI_SR_BSY); SPI1->CR1 &= ~SPI_CR1_SPE; __enable_irq(); } void ENC28J60_WriteOP(uint8_t op, uint8_t addr, uint8_t data) { __disable_irq(); SPI1->CR1 |= SPI_CR1_SPE; SPI1->DR = op | (addr & 0x1f); // 發送操作碼和寄存器地址 while ((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = data; // 發送數據 while ((SPI1->SR & SPI_SR_TXE) == 0); while (SPI1->SR & SPI_SR_BSY); data = SPI1->DR; // 清RxNE SPI1->CR1 &= ~SPI_CR1_SPE; __enable_irq(); } |
![]() |
現在需要把數碼管上的數字顯示到網頁上。打開httptest.c文件,按提示修改代碼:
#include <string.h> #include "lwip/tcp.h" // 一般情況下需要包含的頭文件 #define STR_AND_SIZE(str) (str), strlen(str) extern uint8_t num; // ★導入main.c文件中的全局變量 err_t http_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { // ............ if (p != NULL) { // 提取頁面名稱 // ................. // 生成HTML內容 num++; // ★網頁上顯示的數字應該和數碼管上的一樣 sprintf(str, "<meta charset=\"gb2312\"><title>獲取網頁名稱</title><div style=\"font-family:Arial\"><b>請求的網頁文件名稱是: </b>%s<br><b>num=</b>%d</div>", name, num); // ★修改網頁內容 .............................. |
![]() |
不過,如果收發大量的數據包,ENC28J60_WriteBuf或ENC28J60_ReadBuf的執行時間過長,那麼中斷就會關閉很長的時間,這樣數碼管就有可能閃爍。為了改善這種情況,可以考慮把SPI的操作都改成中斷的形式,並且該中斷的搶占優先級要比數碼管的高,並注意不影響74HC595的時序,這樣效果就要好得多。
|
![]() |
最後為了保險起見,最好在主程序中使用獨立看門狗IWDG,並及時餵狗。萬一程序由於某種原因死機了,可以自動恢復過來而不至於伺服器宕機導致網頁打不開。
可以把自動分配的IP位址和數碼管上顯示的數字放到BKP備用寄存器裡面,復位之後不會丟失。 |
![]() |
【不關中斷時程序卡死在SPI的while循環里的原因】
程序流程如下(連續發送模式): 送數據1; 等DR變為空(TXE=0); 送數據2; 等數據1發送完畢(RXNE=0); (1) 接收數據1; (2) 等數據2發送完畢(RXNE=0); (3) 接收數據2; 如果數據2已經發送完畢了數據1都還沒讀取,那麼數據1將會丟失。 由於沒有關閉中斷,那麼在等待數據1發送完畢(步驟1)時進了數碼管掃描中斷,那麼等到中斷執行完畢後,數據2早就發送完畢了,因此步驟2實際接收到的是數據2而非數據1,數據1已經丟失。並且由於一直沒有新的數據到來,RXNE始終不會被置1,因此程序就卡死在了(3)裡面。 |