色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

單片機沒有串口時如何打印調試信息

GReq_mcu168 ? 來源:STM32嵌入式開發 ? 作者:STM32嵌入式開發 ? 2022-03-10 17:19 ? 次閱讀

輸出調試信息是嵌入式開發中必不可少的調試利器,嵌入式開發的一個特點是很多時候沒有操作系統,或者沒有文件系統,常規的打印log到文件的方法基本不適用。

最常用的是通過串口輸出uart log,例如51單片機,只要實現串口驅動,通過串口輸出就可以了。

串口這種方法實現簡單,大部分嵌入式芯片都有串口功能。但是這樣簡單的功能有時候卻不是那么好用,比如:

  • 一款新拿到的芯片,沒有串口驅動時如何打印log?

  • 某些應用下對時序要求比較高,串口輸出log占用時間太長怎么辦?比如usb枚舉。

  • 某些bug正常運行時會出現,當打開串口log時又不再復現怎么辦?

  • 一些封裝中沒有串口,或者串口已經被用作其他用途,要如何輸出log?

本文介紹單片機沒有串口時,如何打印調試信息。

1 輸出log信息到SRAM

準確來說這里并不是輸出log,而是以一種方式不使用串口就可以看到log。在芯片開發階段都可以連接仿真器調試,可以使用打斷點的方法調試,但是有些操作如果不能被打斷就沒法使用斷點調試了。這時候可以考慮將log打印到SRAM中,整個操作結束后再通過仿真器查看SRAM中的log buffer,這樣就實現了間接的log輸出。

本文使用的測試平臺是stm32f407 discovery,基于usb host實驗代碼,對于其他嵌入式平臺原理也是通用的。

首先定義一個結構體用于打印log,如下:

typedef struct {   volatile u8     type;   u8*             buffer;             /* log buffer指針*/   volatile u32    write_idx;          /* log寫入位置*/   volatile u32    read_idx;           /* log 讀取位置*/}log_dev;

定義一段SRAM空間作為log buffer

static u8 log_buffer[LOG_MAX_LEN];

log buffer是環形緩沖區,在小的buffer就可以無限打印log,缺點也很明顯,如果log沒有及時輸出就會被新的覆蓋。Buffer大小根據SRAM大小分配,這里使用1kB。

為了方便輸出參數,使用printf函數來格式化輸出,需要做如下配置。

單片機沒有串口時如何打印調試信息

并包含頭文件#include , 在代碼中實現函數fputc()。

//redirect fputcint fputc(int ch, FILE *f){    print_ch((u8)ch);    return ch;}

寫入數據到Sram:

/*write log to bufffer or I/O*/void print_ch(u8 ch){    log_dev_ptr->buffer[log_dev_ptr->write_idx++] = ch;    if(log_dev_ptr->write_idx >= LOG_MAX_LEN){        log_dev_ptr->write_idx = 0;    }}

為了方便控制log打印格式,在頭文件中再添加自定義的打印函數:

#ifdef DEBUG_LOG_EN#define DEBUG(...)      printf("usb_printer:"__VA_ARGS__)#else#define DEBUG(...)#endif

在需要打印log的地方直接調用DEBUG()即可,最終效果如下,從Memory窗口可以看到打印的log:

單片機沒有串口時如何打印調試信息

通過SWO輸出log

通過打印log到SRAM的方式可以看到log,但是數據量多的時候可能來不及查看就被覆蓋了。為了解決這個問題,可以使用St-link的SWO輸出log,這樣就不用擔心log被覆蓋。

單片機沒有串口時如何打印調試信息

在log結構體中添加SWO的操作函數集:

typedef struct{    u8 (*init)(void* arg);    u8 (*print)(u8 ch);    u8 (*print_dma)(u8* buffer, u32 len);}log_func;
typedef struct {    volatile u8     type;    u8*             buffer;    volatile u32    write_idx;    volatile u32    read_idx;    //SWO    log_func*       swo_log_func;}log_dev;

SWO只需要print操作函數,實現如下:

u8 swo_print_ch(u8 ch){    ITM_SendChar(ch);    return 0;}

使用SWO輸出log同樣先輸出到log buffer,然后在系統空閑時再輸出,當然也可以直接輸出。log延遲輸出會影響log的實時性,而直接輸出會影響到對時間敏感的代碼運行,所以如何取舍取決于需要輸出log的情形。在while循環中調用output_ch()函數,就可以實現在系統空閑時輸出log。

/*output log buffer to I/O*/void output_ch(void){       u8 ch;    volatile u32 tmp_write,tmp_read;    tmp_write = log_dev_ptr->write_idx;    tmp_read = log_dev_ptr->read_idx;
    if(tmp_write != tmp_read){            ch = log_dev_ptr->buffer[tmp_read++];            //swo            if(log_dev_ptr->swo_log_func)                    log_dev_ptr->swo_log_func->print(ch);            if(tmp_read >= LOG_MAX_LEN){                    log_dev_ptr->read_idx = 0;            }else{                    log_dev_ptr->read_idx = tmp_read;            }    }}

1 通過IDE輸出

使用IDE中SWO輸出功能需要做如下配置(Keil):

單片機沒有串口時如何打印調試信息

在窗口可以看到輸出的log:

單片機沒有串口時如何打印調試信息

2 通過STM32 ST-LINK Utility輸出

使用STM32 ST-LINK Utility不需要做特別的設置,直接打開ST-LINK菜單下的Printf via SWO viewer,然后按start:

單片機沒有串口時如何打印調試信息

通過串口輸出log

以上都是在串口log暫時無法使用,或者只是臨時用一下的方法,而適合長期使用的還是需要通過串口輸出log,畢竟大部分時候沒法連接仿真器。

添加串口輸出log只需要添加串口的操作函數集即可:

typedef struct {    volatile u8     type;    u8*             buffer;    volatile u32    write_idx;    volatile u32    read_idx;    volatile u32    dma_read_idx;    //uart    log_func*       uart_log_func;    //SWO    log_func*       swo_log_func;}log_dev;

實現串口驅動函數:

log_func uart_log_func = {    uart_log_init,    uart_print_ch,    0,};

添加串口輸出log與通過SWO過程類似,不再多敘述。而下面要討論的問題是,串口的速率較低,輸出數據需要較長時間,嚴重影響系統運行。雖然可以通過先打印到SRAM再延時輸出的辦法來減輕影響,但是如果系統中斷頻繁,或者需要做耗時運算,則可能會丟失log。要解決這個問題,就是要解決CPU與輸出數據到串口同時進行的問題,嵌入式工程師立馬可以想到DMA正是好的解決途徑。

使用DMA搬運log數據到串口輸出,同時又不影響CPU運行,這樣就可以解決輸出串口log耗時影響系統的問題:STM32串口收發數據為什么要使用DMA?串口及DMA初始化函數如下:

u8 uart_log_init(void* arg){    DMA_InitTypeDef DMA_InitStructure;    u32* bound = (u32*)arg;    //GPIO端口設置    GPIO_InitTypeDef GPIO_InitStructure;    USART_InitTypeDef USART_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時鐘    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2時鐘    //串口2對應引腳復用映射    GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);    //USART2端口配置    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHz    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉    GPIO_Init(GPIOA,&GPIO_InitStructure); //USART2初始化設置    USART_InitStructure.USART_BaudRate = *bound;//波特率設置    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位    USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制    USART_InitStructure.USART_Mode = USART_Mode_Tx; //收發模式    USART_Init(USART2, &USART_InitStructure); //初始化串口1#ifdef LOG_UART_DMA_EN      USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);#endif      USART_Cmd(USART2, ENABLE);  //使能串口1     USART_ClearFlag(USART2, USART_FLAG_TC);    while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);#ifdef LOG_UART_DMA_EN    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);    //Config DMA channel, uart2 TX usb DMA1 Stream6 Channel    DMA_DeInit(DMA1_Stream6);    DMA_InitStructure.DMA_Channel = DMA_Channel_4;    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DR);    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;    DMA_InitStructure.DMA_Priority = DMA_Priority_High;    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    DMA_Init(DMA1_Stream6, &DMA_InitStructure);    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);#endif    return 0;}

DMA輸出到串口的函數如下:

u8 uart_print_dma(u8* buffer, u32 len){        if((DMA1_Stream6->CR & DMA_SxCR_EN) != RESET){                //dma not ready                return 1;        }        if(DMA_GetFlagStatus(DMA1_Stream6,DMA_IT_TCIF6) != RESET){                DMA_ClearFlag(DMA1_Stream6,DMA_FLAG_TCIF6);                DMA_Cmd(DMA1_Stream6,DISABLE);        }        DMA_SetCurrDataCounter(DMA1_Stream6,len);        DMA_MemoryTargetConfig(DMA1_Stream6, (u32)buffer, DMA_Memory_0);        DMA_Cmd(DMA1_Stream6,ENABLE);        return 0;}

這里為了方便直接使用了查詢DMA狀態寄存器,有需要可以修改為DMA中斷方式,查Datasheet可以找到串口2使用DMA1 channel4的stream6:

單片機沒有串口時如何打印調試信息

最后在PC端串口助手可以看到log輸出:

單片機沒有串口時如何打印調試信息

使用DMA搬運log buffer中數據到串口,同時CPU可以處理其他事情,這種方式對系統影響最小,并且輸出log及時,是實際使用中用的最多的方式。并且不僅可以用串口,其他可以用DMA操作的接口(如SPI、USB)都可以使用這種方法來打印log。

使用IO模擬串口輸出log

最后要討論的是在一些封裝中沒有串口,或者串口已經被用作其他用途時如何輸出log,這時可以找一個空閑的普通IO,模擬UART協議輸出log到上位機的串口工具。

常用的UART協議如下:

單片機沒有串口時如何打印調試信息

只要在確定的時間在IO上輸出高低電平就可以模擬出波形,這個確定的時間就是串口波特率。

為了得到精確延時,這里使用TIM4定時器產生1us的延時。注意:定時器不能重復用,在測試工程中TIM2、3都被用了,如果重復用就錯亂了。

初始化函數如下:

u8 simu_log_init(void* arg){    TIM_TimeBaseInitTypeDef TIM_InitStructure;      u32* bound = (u32*)arg;    //GPIO端口設置    GPIO_InitTypeDef GPIO_InitStructure;    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時鐘    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHz    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉    GPIO_Init(GPIOA,&GPIO_InitStructure);    GPIO_SetBits(GPIOA, GPIO_Pin_2);    //Config TIM    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //使能TIM4時鐘    TIM_DeInit(TIM4);    TIM_InitStructure.TIM_Prescaler = 1;        //2分頻    TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;    TIM_InitStructure.TIM_Period = 41;          //1us timer    TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    TIM_TimeBaseInit(TIM4, &TIM_InitStructure);    TIM_ClearFlag(TIM4, TIM_FLAG_Update);    baud_delay = 1000000/(*bound);          //根據波特率計算每個bit延時    return 0;}

使用定時器的delay函數為:

void simu_delay(u32 us){    volatile u32 tmp_us = us;    TIM_SetCounter(TIM4, 0);    TIM_Cmd(TIM4, ENABLE);    while(tmp_us--){        while(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) == RESET);        TIM_ClearFlag(TIM4, TIM_FLAG_Update);    }       TIM_Cmd(TIM4, DISABLE);}

最后是模擬輸出函數,注意:輸出前必須要關閉中斷,一個byte輸出完再打開,否則會出現亂碼:

u8 simu_print_ch(u8 ch){   volatile u8 i=8;   __asm("cpsid i");   //start bit   GPIO_ResetBits(GPIOA, GPIO_Pin_2);   simu_delay(baud_delay);   while(i--){           if(ch & 0x01)               GPIO_SetBits(GPIOA, GPIO_Pin_2);           else               GPIO_ResetBits(GPIOA, GPIO_Pin_2);           ch >>= 1;           simu_delay(baud_delay);   }   //stop bit   GPIO_SetBits(GPIOA, GPIO_Pin_2);   simu_delay(baud_delay);   simu_delay(baud_delay);   __asm("cpsie i");   return 0;}

介紹了幾種開發中使用過的打印調試信息的方法,方法總是死的,關鍵在于能靈活使用;通過打印有效的調試信息,可以幫助解決開發及后期維護中遇到的問題,少走彎路。

如果是你在項目中,沒有串口線你會怎么調試呢?請在評論區說出你的想法。

原文標題:沒有串口,如何打印單片機調試信息?

文章出處:【微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 單片機
    +關注

    關注

    6042

    文章

    44617

    瀏覽量

    637604
  • 調試
    +關注

    關注

    7

    文章

    589

    瀏覽量

    34031
  • 串口
    +關注

    關注

    14

    文章

    1557

    瀏覽量

    76872

原文標題:沒有串口,如何打印單片機調試信息?

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    單片機串口模塊調試方法

    筆者在調試某Cortex-M3內核單片機時遇到一個問題,此單片機不具備在線仿真功能,因此調試代碼時只能使用UART輸出Log的方式調試
    發表于 07-28 15:48 ?1610次閱讀
    <b class='flag-5'>單片機</b><b class='flag-5'>串口</b>模塊<b class='flag-5'>調試</b>方法

    請問單片機和GSM模塊通信使用的串口可以同時使用在電腦上打印調試信息嗎?

    單片機和GSM模塊通信使用的串口,還能同時使用在電腦上打印調試信息不?搞了半天沒現象,是這個原因不?一個
    發表于 03-14 06:35

    如何在STM32單片機上實現Printf函數打印串口信息并進行代碼調試

    如何在STM32單片機上實現Printf函數打印串口信息并進行代碼調試
    發表于 12-02 07:35

    單片機串口調試助手程序下載

    單片機串口調試助手程序下載,喜歡的朋友可以下載來學習。
    發表于 01-12 14:56 ?49次下載

    單片機多功能調試助手

    單片機多功能調試助手 串口、USB等調試用。
    發表于 03-11 10:34 ?8次下載

    STM8S串口打印調試信息(不使用printf)

    STM8S串口打印調試信息(不使用printf),感興趣可以看看。
    發表于 07-25 18:52 ?51次下載

    如何進行單片機串口調試詳細實例說明

    串口調試助手 發送一字節的數據,單片機馬上會回發同樣的數據在串口調試助手上顯示,如果回發給串口
    發表于 07-08 17:14 ?1次下載
    如何進行<b class='flag-5'>單片機</b>的<b class='flag-5'>串口</b><b class='flag-5'>調試</b>詳細實例說明

    51單片機串口通訊詳解

    串口,作為單片機程序開發中最常用、最方便,也是應用最廣泛的程序調試方法;無論是作為調試工具,打印出調試
    發表于 11-11 17:06 ?15次下載
    51<b class='flag-5'>單片機</b><b class='flag-5'>串口</b>通訊詳解

    51單片機串口通信調試printf函數重定向輸出打印

    格式化輸出串行通信不知道你們寫單片機程序的時候有沒有煩惱,它沒有想c語言的dos窗口,沒有java的運行窗口,沒有python的控制臺,有時
    發表于 11-20 16:36 ?16次下載
    51<b class='flag-5'>單片機</b><b class='flag-5'>串口</b>通信<b class='flag-5'>調試</b>printf函數重定向輸出<b class='flag-5'>打印</b>

    可以實時打印串口信息串口調試軟件

    可以實時打印串口信息串口調試軟件
    發表于 11-23 18:17 ?15次下載

    《電子DIY》之藍牙的使用,Proteus仿真單片機串口的注意事項和實物調試注意事項。玩轉單片機串口詳細零基礎版

    藍牙的使用,Proteus仿真單片機串口的注意事項和實物調試注意事項。玩轉單片機串口詳細零基礎版學習單片
    發表于 12-29 19:48 ?0次下載
    《電子DIY》之藍牙的使用,Proteus仿真<b class='flag-5'>單片機</b><b class='flag-5'>串口</b>的注意事項和實物<b class='flag-5'>調試</b>注意事項。玩轉<b class='flag-5'>單片機</b><b class='flag-5'>串口</b>詳細零基礎版

    如何用調試器JLink來打印信息

    摘要:不知道大家在單片機開發中是如何打印調試信息的,大多數應該是用串口調試
    的頭像 發表于 10-14 09:55 ?3703次閱讀

    stm32f103zet6單片機串口互發程序

    為什么用51單片機調試串口藍牙模塊或者是串口wifi模塊很困難呢?因為串口只有一個,串口一旦用于
    發表于 01-05 15:44 ?6次下載

    單片機常用的調試方法

    單片機程序調試過程中,串口打印調試方法是非常重要的手段,在使用串口
    的頭像 發表于 04-04 14:58 ?4985次閱讀

    51單片機串口配置方法

    串口,作為單片機程序開發中最常用、最方便,也是應用最廣泛的程序調試方法;無論是作為調試工具,打印出調試
    的頭像 發表于 04-14 14:58 ?5288次閱讀
    51<b class='flag-5'>單片機</b><b class='flag-5'>串口</b>配置方法
    主站蜘蛛池模板: 久久草香蕉频线观 | 看美女大腿中间的部分 | 国产精品亚洲在钱视频 | 亚洲精品国产A久久久久久 亚洲精品国产AV成人毛片 | 在线免费观看国产精品 | 男女交性视频无遮挡全过程 | 2017欧美狠狠色 | 厨房玩朋友娇妻中文字幕 | 亚洲欧美日韩中字视频三区 | 海角社区在线视频播放观看 | 亚洲区视频在线观看 | 亚洲国产AV一区二区三区四区 | 成人在线观看视频免费 | 麻豆XXXX乱女少妇精品-百度 | 欧美日韩亚洲第一区在线 | 精品国产乱码久久久久乱码 | 亚洲免费一区二区 | 日本50人群体交乱 | 国产人妻精品无码AV在线五十路 | 青青热久精品国产亚洲AV无码 | 毛片在线看片 | 久久青青草原精品国产软件 | 男男腐文污高干嗯啊快点1V1 | 国产精品嫩草影院 | jizz丝袜| 久久偷拍免费2017 | 琉璃美人煞在线观看 | 欧美激情一区二区三区视频 | 成人免费观看www视频 | 乌克兰14一18处交见血 | 国产精品女上位好爽在线短片 | 午夜成a人片在线观看 | 国产精品久久久久久精品... | 泷泽萝拉首部av | 精品午夜视频 | 3344永久在线观看视频免费 | 少妇无码吹潮久久精品AV网站 | 丰满少妇67194视频 | 日韩1区1区产品乱码芒果榴莲 | 99久久精品费精品国产 | 欧美无码专区 |