設置 | 登錄 | 註冊

目前共有11篇帖子。

【程序】使用數碼管顯示lwip網頁訪問的次數

1樓 巨大八爪鱼 2017-4-2 19:41
修改前的工程:https://zh.arslanbar.net/post.php?t=24586
【最終效果】
打開網頁時,自動把數字加1,並顯示數碼管上顯示的數字。
例如訪問網頁「http://enc28j60/wel」時,顯示的內容如下:
請求的網頁文件名稱是: /wel
num=170

同時,數碼管動態掃描顯示數字170。
2樓 巨大八爪鱼 2017-4-2 19:45
首先打開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; // 開定時器
3樓 巨大八爪鱼 2017-4-2 19:47

在main函數下添加定時器7的中斷服務函數:
void TIM7_IRQHandler(void)
{
    static uint8_t i = 0;
    static uint8_t numbuf;
    TIM7->SR &= ~TIM_SR_UIF;
    if (i == 0)
        numbuf = num;
    ser_in(seg8[numbuf % 10]);
    ser_in(1 << i);
    par_out();
    numbuf /= 10;
    i = (i + 1) % 3; // 總共3個數碼管
}

該函數每次只點亮一個數碼管,從低位掃描到高位。

4樓 巨大八爪鱼 2017-4-2 19:54

本程序的關鍵是解決數碼管掃描中斷打斷lwip和網卡驅動原本的程序而卡死的問題。有兩種解決方法:
1. 在main函數的主循環中開關中斷:
while (1)
{
  __disable_irq();
  // lwip處理
  __enable_irq();
}
2. 把上述語句添加在網卡驅動程序中與SPI有關的函數裡。主要是ENC28J60.c文件中如下4個函數:
ENC28J60_ReadBuf
ENC28J60_ReadOP
ENC28J60_WriteBuf
ENC28J60_WriteOP

顯然方法2要更好一些,畢竟關中斷的時間要比方法1短得多。數碼管的掃描也就不容易出現閃爍。

5樓 巨大八爪鱼 2017-4-2 19:57
採用方法2後,就算在瀏覽器里一直按著F5刷新鍵不放,程序也不會卡死,並且數碼管絲毫沒有閃爍。
6樓 巨大八爪鱼 2017-4-2 19:58
【修改後的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();
}
7樓 巨大八爪鱼 2017-4-2 20:01
現在需要把數碼管上的數字顯示到網頁上。打開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); // ★修改網頁內容

..............................
8樓 巨大八爪鱼 2017-4-2 20:05
不過,如果收發大量的數據包,ENC28J60_WriteBuf或ENC28J60_ReadBuf的執行時間過長,那麼中斷就會關閉很長的時間,這樣數碼管就有可能閃爍。為了改善這種情況,可以考慮把SPI的操作都改成中斷的形式,並且該中斷的搶占優先級要比數碼管的高,並注意不影響74HC595的時序,這樣效果就要好得多。
9樓 巨大八爪鱼 2017-4-2 20:07
最後為了保險起見,最好在主程序中使用獨立看門狗IWDG,並及時餵狗。萬一程序由於某種原因死機了,可以自動恢復過來而不至於伺服器宕機導致網頁打不開。
可以把自動分配的IP位址和數碼管上顯示的數字放到BKP備用寄存器裡面,復位之後不會丟失。
10樓 巨大八爪鱼 2017-4-12 19:30
【不關中斷時程序卡死在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)裡面。

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。