一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

不用串口,如何打印STM32單片機log

STM32嵌入式開發(fā) ? 來源:STM32嵌入式開發(fā) ? 作者:STM32嵌入式開發(fā) ? 2022-11-18 11:37 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本文主要介紹在嵌入式開發(fā)中用來輸出log的方法。

最常用的是通過串口輸出uart log,這種方法實現(xiàn)簡單,大部分嵌入式芯片都有串口功能。但是這樣簡單的功能有時候卻不是那么好用,比如:

一款新拿到的芯片,沒有串口驅(qū)動時如何打印log

某些應(yīng)用下對時序要求比較高,串口輸出log占用時間太長怎么辦?比如USB枚舉。

某些bug正常運行時會出現(xiàn),當(dāng)打開串口log時又不再復(fù)現(xiàn)怎么辦

一些封裝中沒有串口,或者串口已經(jīng)被用作其他用途,要如何輸出log

下文來討論這些問題。

1輸出log信息到SRAM

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

本文使用的測試平臺是STM32F407 discovery,基于usb host實驗代碼,對于其他嵌入式平臺原理也是通用的。首先定義一個結(jié)構(gòu)體用于打印log,如下:

ccfa1872-6658-11ed-8abf-dac502259ad0.png

定義一段SRAM空間作為log buffer:


static u8 log_buffer[LOG_MAX_LEN];

log buffer是環(huán)形緩沖區(qū),在小的buffer就可以無限打印log,缺點也很明顯,如果log沒有及時輸出就會被新的覆蓋。Buffer大小根據(jù)SRAM大小分配,這里使用1kB。為了方便輸出參數(shù),使用printf函數(shù)來格式化輸出,需要做如下配置(Keil):

cd0bd724-6658-11ed-8abf-dac502259ad0.png

并包含頭文件#include , 在代碼中實現(xiàn)函數(shù)fputc():

cd298d8c-6658-11ed-8abf-dac502259ad0.png

寫入數(shù)據(jù)到SRAM:

cd3aab44-6658-11ed-8abf-dac502259ad0.png

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

cd4e81dc-6658-11ed-8abf-dac502259ad0.png

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

cd663138-6658-11ed-8abf-dac502259ad0.png

2通過SWO輸出log

通過打印log到SRAM的方式可以看到log,但是數(shù)據(jù)量多的時候可能來不及查看就被覆蓋了。為了解決這個問題,可以使用St-link的SWO輸出log,這樣就不用擔(dān)心log被覆蓋。查看原理圖f407 discovery的SWO已經(jīng)連接了,否則需要自己飛線連接:

cd7a09d8-6658-11ed-8abf-dac502259ad0.png

在log結(jié)構(gòu)體中添加SWO的操作函數(shù)集:


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操作函數(shù),實現(xiàn)如下:


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

使用SWO輸出log同樣先輸出到log buffer,然后在系統(tǒng)空閑時再輸出,當(dāng)然也可以直接輸出。log延遲輸出會影響log的實時性,而直接輸出會影響到對時間敏感的代碼運行,所以如何取舍取決于需要輸出log的情形。

在while循環(huán)中調(diào)用output_ch()函數(shù),就可以實現(xiàn)在系統(tǒng)空閑時輸出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;
        }
    }
}

2.1 通過IDE輸出

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

cd9bc870-6658-11ed-8abf-dac502259ad0.png

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

cdc08214-6658-11ed-8abf-dac502259ad0.png

2.2 通過STM32 ST-LINK Utility輸出

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

cdcf8d90-6658-11ed-8abf-dac502259ad0.png

3通過串口輸出log

以上都是在串口log暫時無法使用,或者只是臨時用一下的方法,而適合長期使用的還是需要通過串口輸出log,畢竟大部分時候沒法連接仿真器。添加串口輸出log只需要添加串口的操作函數(shù)集即可:


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;

實現(xiàn)串口驅(qū)動函數(shù):

cde52b78-6658-11ed-8abf-dac502259ad0.png

添加串口輸出log與通過SWO過程類似,不再多敘述。而下面要討論的問題是,串口的速率較低,輸出數(shù)據(jù)需要較長時間,嚴(yán)重影響系統(tǒng)運行。

雖然可以通過先打印到SRAM再延時輸出的辦法來減輕影響,但是如果系統(tǒng)中斷頻繁,或者需要做耗時運算,則可能會丟失log。要解決這個問題,就是要解決CPU與輸出數(shù)據(jù)到串口同時進(jìn)行的問題,嵌入式工程師立馬可以想到DMA正是好的解決途徑。

使用DMA搬運log數(shù)據(jù)到串口輸出,同時又不影響CPU運行,這樣就可以解決輸出串口log耗時影響系統(tǒng)的問題。串口及DMA初始化函數(shù)如下:


u8 uart_log_init(void* arg)
{
    DMA_InitTypeDef DMA_InitStructure;
    u32* bound = (u32*)arg;
//GPIO端口設(shè)置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;


    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時鐘
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2時鐘
//串口2對應(yīng)引腳復(fù)用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);
//USART2端口配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復(fù)用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復(fù)用輸出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure);
//USART2初始化設(shè)置
    USART_InitStructure.USART_BaudRate = *bound;//波特率設(shè)置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
    USART_InitStructure.USART_Mode = USART_Mode_Tx; //收發(fā)模式
    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輸出到串口的函數(shù)如下:

cdfd94e2-6658-11ed-8abf-dac502259ad0.png

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

ce0fab32-6658-11ed-8abf-dac502259ad0.png

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

ce2211aa-6658-11ed-8abf-dac502259ad0.png

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

4使用IO口模擬串口輸出log

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

ce326b5e-6658-11ed-8abf-dac502259ad0.png

只要在確定的時間在IO上輸出高低電平就可以模擬出波形,這個確定的時間就是串口波特率。為了得到精確延時,這里使用TIM4定時器產(chǎn)生1us的延時。注意:定時器不能重復(fù)用,在測試工程中TIM2、3都被用了,如果重復(fù)用就錯亂了。初始化函數(shù)如下:


u8 simu_log_init(void* arg)
{
    TIM_TimeBaseInitTypeDef TIM_InitStructure;  
    u32* bound = (u32*)arg;
//GPIO端口設(shè)置
    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; //推挽復(fù)用輸出
    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);          //根據(jù)波特率計算每個bit延時
return 0;
}

使用定時器的delay函數(shù)為:

cf20b214-6658-11ed-8abf-dac502259ad0.png

最后是模擬輸出函數(shù),注意:輸出前必須要關(guān)閉中斷,一個byte輸出完再打開,否則會出現(xiàn)亂碼:


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;
}

使用IO模擬可以達(dá)到與真實串口類似的效果,并且只需要一個普通IO,在小封裝芯片上比較使用。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關(guān)注

    關(guān)注

    6067

    文章

    44981

    瀏覽量

    650263
  • 嵌入式
    +關(guān)注

    關(guān)注

    5149

    文章

    19657

    瀏覽量

    317341
  • STM32
    +關(guān)注

    關(guān)注

    2293

    文章

    11031

    瀏覽量

    364670
  • Log
    Log
    +關(guān)注

    關(guān)注

    0

    文章

    16

    瀏覽量

    11542

原文標(biāo)題:不用串口,如何打印STM32單片機log

文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    STM32單片機串口接收數(shù)據(jù)的方法

    串口作為單片機開發(fā)的一個常用的外設(shè),應(yīng)用范圍非常廣。大部分時候,串口需要接收處理的數(shù)據(jù)長度是不定的。那么怎么才能判斷一幀數(shù)據(jù)是否結(jié)束呢,今天就以STM32
    發(fā)表于 09-21 14:39 ?1.3w次閱讀

    單片機串口模塊調(diào)試方法

    筆者在調(diào)試某Cortex-M3內(nèi)核單片機時遇到一個問題,此單片機不具備在線仿真功能,因此調(diào)試代碼時只能使用UART輸出Log的方式調(diào)試。調(diào)試過程中發(fā)現(xiàn)串口模塊會影響
    發(fā)表于 07-28 15:48 ?1931次閱讀
    <b class='flag-5'>單片機</b><b class='flag-5'>串口</b>模塊調(diào)試方法

    有哪幾種辦法可實現(xiàn)單片機像在pc終端一樣打印log

    有哪幾種辦法可實現(xiàn)單片機像在pc終端一樣打印log呢?stm32實現(xiàn)printf打印log的辦法
    發(fā)表于 12-01 06:39

    單片機是如何實現(xiàn)printf打印串口

    軟件顯示區(qū)了! 和電腦端一樣用!串口初始化代碼部分,以STM32為例,其他單片機也一樣,只是修改成對應(yīng)的單片機寄存器即可,整個邏輯是一樣的若只是實現(xiàn)printf
    發(fā)表于 02-16 07:10

    STM32 單片機串口通信仿真測試技術(shù)研究_郭勇.pdf下載

    STM32單片機串口通信仿真測試技術(shù)
    發(fā)表于 04-22 11:02 ?14次下載
    <b class='flag-5'>STM32</b> <b class='flag-5'>單片機</b>多<b class='flag-5'>串口</b>通信仿真測試技術(shù)研究_郭勇.pdf下載

    基于STM32單片機串口通信資源

    基于STM32單片機串口通信資源
    發(fā)表于 07-05 09:13 ?21次下載

    STM32 LoRa無線數(shù)傳模塊 PC通過串口傳輸數(shù)據(jù)到單片機

    STM32F1單片機,燒錄代碼后,連接LoRa無線數(shù)傳模塊,在PC上面使用串口助手,通過串口傳輸數(shù)據(jù)到單片機
    發(fā)表于 11-19 11:51 ?79次下載
    <b class='flag-5'>STM32</b> LoRa無線數(shù)傳模塊 PC通過<b class='flag-5'>串口</b>傳輸數(shù)據(jù)到<b class='flag-5'>單片機</b>

    【轉(zhuǎn)】STC89C52RC單片機實現(xiàn)串口打印功能

    【轉(zhuǎn)】STC89C52RC單片機實現(xiàn)串口打印功能
    發(fā)表于 11-25 16:06 ?19次下載
    【轉(zhuǎn)】STC89C52RC<b class='flag-5'>單片機</b>實現(xiàn)<b class='flag-5'>串口</b><b class='flag-5'>打印</b>功能

    單片機實現(xiàn) printf 打印輸出,和電腦端一樣用

    軟件顯示區(qū)了! 和電腦端一樣用!串口初始化代碼部分,以STM32為例,其他單片機也一樣,只是修改成對應(yīng)的單片機寄存器即可,整個邏輯是一樣的若只是實現(xiàn)printf
    發(fā)表于 12-17 18:32 ?1次下載
    <b class='flag-5'>單片機</b>實現(xiàn) printf <b class='flag-5'>打印</b>輸出,和電腦端一樣用

    單片機自定義串口打印程序

    單片機自定義串口打印程序#include #include void printf(const char* format
    發(fā)表于 12-27 19:19 ?10次下載
    <b class='flag-5'>單片機</b>自定義<b class='flag-5'>串口</b><b class='flag-5'>打印</b>程序

    stm32單片機串口使用printf及u3_printf

    無論是在51單片機還是在stm32,默認(rèn)printf串口都是串口一。使用printf的時候頭文件為&amp;quot;stdio.h&amp;quot;,但是一些
    發(fā)表于 12-27 19:24 ?1次下載
    <b class='flag-5'>stm32</b><b class='flag-5'>單片機</b><b class='flag-5'>串口</b>使用printf及u3_printf

    不用串口,如何打印STM32單片機log?

    本文主要介紹在嵌入式開發(fā)中用來輸出log的方法。
    發(fā)表于 02-08 15:42 ?3次下載
    <b class='flag-5'>不用</b><b class='flag-5'>串口</b>,如何<b class='flag-5'>打印</b><b class='flag-5'>STM32</b><b class='flag-5'>單片機</b><b class='flag-5'>log</b>?

    STM32與51單片機原理圖及串口通信實例

    分別編寫STM32與51單片機程序,通過串口通信,實現(xiàn)STM32按鍵控制51單片機LED的實驗效果。
    發(fā)表于 12-28 14:27 ?9812次閱讀

    stm32f103zet6單片機串口互發(fā)程序

    為什么用51單片機調(diào)試串口藍(lán)牙模塊或者是串口wifi模塊很困難呢?因為串口只有一個,串口一旦用于與模塊通信之后,就沒有辦法進(jìn)行調(diào)試信息的
    發(fā)表于 01-05 15:44 ?6次下載

    51單片機串口配置方法

    串口,作為單片機程序開發(fā)中最常用、最方便,也是應(yīng)用最廣泛的程序調(diào)試方法;無論是作為調(diào)試工具,打印出調(diào)試信息,還是對功能模塊進(jìn)行通信,串口是每個單片機
    的頭像 發(fā)表于 04-14 14:58 ?6264次閱讀
    51<b class='flag-5'>單片機</b><b class='flag-5'>串口</b>配置方法