Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >没有串口,如何打印单片机调试信息?

没有串口,如何打印单片机调试信息?

作者头像
MCU起航
发布于 2022-02-05 08:21:25
发布于 2022-02-05 08:21:25
1.3K00
代码可运行
举报
文章被收录于专栏:单片机爱好者单片机爱好者
运行总次数:0
代码可运行

输出调试信息是嵌入式开发中必不可少的调试利器,嵌入式开发的一个特点是很多时候没有操作系统,或者没有文件系统,常规的打印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,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct {
   volatile u8     type;
   u8*             buffer;             /* log buffer指针*/
   volatile u32    write_idx;          /* log写入位置*/
   volatile u32    read_idx;           /* log 读取位置*/
}log_dev;

定义一段SRAM空间作为log buffer

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static u8 log_buffer[LOG_MAX_LEN];

log buffer是环形缓冲区,在小的buffer就可以无限打印log,缺点也很明显,如果log没有及时输出就会被新的覆盖。Buffer大小根据SRAM大小分配,这里使用1kB。

为了方便输出参数,使用printf函数来格式化输出,需要做如下配置。

并包含头文件#include <stdio.h>, 在代码中实现函数fputc()。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//redirect fputc
int fputc(int ch, FILE *f)
{
    print_ch((u8)ch);
    return ch;
}

写入数据到Sram:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*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打印格式,在头文件中再添加自定义的打印函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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的操作函数集:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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操作函数,实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
u8 swo_print_ch(u8 ch)
{
    ITM_SendChar(ch);
    return 0;
}

使用SWO输出log同样先输出到log buffer,然后在系统空闲时再输出,当然也可以直接输出。log延迟输出会影响log的实时性,而直接输出会影响到对时间敏感的代码运行,所以如何取舍取决于需要输出log的情形。在while循环中调用output_ch()函数,就可以实现在系统空闲时输出log。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*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只需要添加串口的操作函数集即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;

实现串口驱动函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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初始化函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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输出到串口的函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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都被用了,如果重复用就错乱了。

初始化函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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函数为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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输出完再打开,否则会出现乱码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}

介绍了几种开发中使用过的打印调试信息的方法,方法总是死的,关键在于能灵活使用;通过打印有效的调试信息,可以帮助解决开发及后期维护中遇到的问题,少走弯路。

如果是你在项目中,没有串口线你会怎么调试呢?请在评论区说出你的想法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 单片机爱好者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
没有串口,如何打印单片机调试信息?
输出调试信息是嵌入式开发中必不可少的调试利器,嵌入式开发的一个特点是很多时候没有操作系统,或者没有文件系统,常规的打印log到文件的方法基本不适用。
混说Linux
2023/02/24
1.4K0
没有串口,如何打印单片机调试信息?
STM32-正弦波可调(50HZ~20KHZ可调、峰峰值0~3.3V可调)
1.原理: 通过定时器每隔一段时间触发一次DAC转换,然后通过DMA发送正玄波码表值给DAC. 当需要改变频率HZ时,只需要修改定时器频率即可(最高只能达到20KHz) 当需要改变正玄波的正峰峰值/负峰峰值时,只需要修改正玄波码表即可 2.实现 代码如下所示(采用的是定时器2,DAC引脚是PA4) #define HZ(x) (u16)(72000000/sizeof(Sine12bit)*2/x) //计算Hz #define DAC_DHR12R1 0x40007408 //外设DAC通道1的
诺谦
2018/04/17
2.9K1
STM32-正弦波可调(50HZ~20KHZ可调、峰峰值0~3.3V可调)
基于STM32F4单片机对步进电机的控制(有代码)「建议收藏」
步进电机是将电脉冲控制信号转变为角位移或线位移的一种常用的数字控制执行元件,又称为脉冲电机。在驱动电源的作用下,步进电机受到脉冲的控制,其转子的角位移量和速度严格地与输入脉冲的数量和脉冲频率成正比。步进电机每接收一个电脉冲,转子就转过一个相应的角度(步距角)。**改变通电顺序可改变步进电动机的旋转方向;改变通电频率可改变步进电动机的转速。**因此,通过控制输入电脉冲的数目、频率及电动机绕组的通电顺序就可以获得所需要的转角、转速及转向,利用单片机就可以很容易实现步进电机的开环数字控制。 传统的步进电机控制方法是由触发器产生控制脉冲来进行控制的,但此种控制方法工作方式单一而且难于实现人机交互,当步进电机的参数发生变化时,需要重新进行控制器的设计。因此适合于单片机控制,单片机通过向步进电机驱动电路发送控制信号就能实现对步进电机的控制。
全栈程序员站长
2022/08/23
9.2K2
基于STM32F4单片机对步进电机的控制(有代码)「建议收藏」
战舰V3 USART1、USART2、UASRT3、UART4、UART5串口配置
在STM32中UART和USART是不相同的,在官方的文档中,大部分配置的都是USART2和UASRT3,对于UART4和UART5却很少有人配置,由于最近在集成项目,所以要用到多种串口,所以索性就配置了UART4和UART5
緣來
2020/01/02
2.2K0
战舰V3 USART1、USART2、UASRT3、UART4、UART5串口配置
(26)STM32——内部温度传感器笔记
目录 学习目标 成果展示  介绍 原理 配置 代码  总结  ---- 学习目标         本节内容是内部温度传感器,其实主要原理还是ADC,和我们上节内容相似,只不过改动了一点点,不过在这之前我们需要先来介绍一下内部温度传感器的原理,这个与学51单片机时接触的DS13B20不太相同。 成果展示  https://live.csdn.net/v/embed/233548 内部温度传感器 介绍 STM32F407 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。 该温度传感器在
小点点
2022/12/12
1.2K0
(26)STM32——内部温度传感器笔记
利用STM32F103精确控制步进电机
STM32F103C8T6 42步进电机(42SHDC4040-17B) TB6600电机驱动器升级版
全栈程序员站长
2022/09/05
2.1K0
利用STM32F103精确控制步进电机
M-Arch(8)第七个示例:串口和DMA
由于之前我们已经使用了串口1(STM中是USART1,GD中是USART0),本文中以串口2作为示例(STM中是USART2,GD中是USART1)。
滚神大人
2022/03/22
6330
M-Arch(8)第七个示例:串口和DMA
关于STM32空闲中断
有一次做一个东西,为了尽量不占用CPU的处理数据时间,所以就使用DMA接收串口的数据,但是呢问题来了.,,,,,怎么样才能确定接收到了一条完整的数据了,,我们都知道只要打开DMA 那家伙就不停的把接收的数据放到我们指定的地方. 只要接收到一条完整的数据我就该去处理了 关于空闲中断,,,就是说每接收到一条完整的数据就会置位空闲标志位,我们只需要判断空闲标志位是否置一,,就能知道是不是接收到了一条完整的数据 用空闲中断的好处就是,,对于以前我写程序通信都会在数据的后面加上尾,,然后另一个接收的单片机通过判断数据
杨奉武
2018/04/18
9440
关于STM32空闲中断
超好用的检测模块---SUI-101A交流变送器
SUI-101A模块是一款互感器隔离的高精度多功能交流变送器,模块介绍如图1所示。该模块可实时测量交流电压、电流、有功功率、功率因数、电量、频率等参数,其中电流和电压的变送精度可达 0.2 级的高精度;并且提供标准通信接口,可选用Modbus协议以及自定义协议。该模块的构建采用全隔离采集方式,能够实现高电压与低电压的隔离,大大增强了安全性及可靠性。
不去幼儿园
2024/12/03
2110
超好用的检测模块---SUI-101A交流变送器
串口USART和UART「建议收藏」
UART是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接受。UART主要用于主机与辅助设备通信。
全栈程序员站长
2022/08/31
2.4K0
STM32—无需中断来实现使用DMA接收串口数据
诺谦
2018/01/03
4.3K0
STM32—无需中断来实现使用DMA接收串口数据
STM32F4 SPI DMA
张贴的代码显示如何配置我的GPIO,定时器,SPI, DMA和NVIC模块,以及一些系统如何工作的解释。 注意,我使用的是STM32F4标准外设库。
小锋学长生活大爆炸
2020/09/24
2.2K0
STM32F4 SPI DMA
(24)STM32——待机唤醒(低功耗)笔记
        这三种模式的功耗是逐渐降低的,特别是待机模式,功耗特别低,最低只需要 2.2uA 左右的电流。停机模式是次低功耗的,其典型的电流消耗在 350uA 左右。最后就是睡眠模式了。根据最低电源消耗,最快启动时间和可用的唤醒源等条件,选择一种最佳的低功耗模式。
小点点
2022/12/12
1.6K0
(24)STM32——待机唤醒(低功耗)笔记
STM32F103RCT6+串口DMA方式接收定长数据
知否知否应是绿肥红瘦
2025/02/19
2230
STM32F103RCT6+串口DMA方式接收定长数据
(40)STM32——OV2640摄像头实验
目录 学习目标 运行结果 内容 OV2640 特点 时序 帧输出时序 配置 DCMI 特点 信号 DMA 寄存器 配置 硬件连接 代码 总结  ---- 学习目标         今天我们要学习的是OV2640摄像头实验,采用的是DCMI接口,进行传输。个人觉得难度较大,加上没有相应的串口线,导致部分实验无法进行,所以就先讲解理论知识,等串口线到了再把电脑端的实验补上。 运行结果 https://live.csdn.net/v/embed/242011 摄像头实验 内容 OV2640        
小点点
2022/12/12
3.2K0
(40)STM32——OV2640摄像头实验
(33)STM32——485实验笔记
目录 学习目标 内容 串口  485 特点 收发器 配置 代码 总结  ---- 学习目标         本节内容我们要学习的是485通信,这个是在51上没有的,但是知识不是特别复杂,而且和我们之前学的串口知识是相似的。 内容 串口  串口是一种接口标准,它规定了接口的电气标准,简单说只是物理层的一个标准。没有规定接口插件电缆以及使用的协议,所以只要我们使用的接口插件电缆符合串口标准就可以在实际中灵活使用,在串口接口标准上使用各种协议进行通讯及设备控制。 典型的串行通讯标准是RS232和RS485
小点点
2022/12/12
6310
(33)STM32——485实验笔记
单片机(MCU)如何才能不死机之串口Overrun
它是可以在 STM32F030 上调试通过的串口收发测试程序,发送采用延时循环,接收采用中断,接收到的数据存入缓冲区。
用户2366192
2021/05/31
1.3K0
(十三)STM32——串口通信(UART)
目录 学习目标 内容 通信方法 并行通信 串行通信 通信方向 通信方式  UART 特点 串口参数 通信流程 寄存器 USART_SR USART_DR  USART_BRR 过程 代码 运行结果 运行结果 遇到的问题 总结  ---- 学习目标         本节我们要学习的的是STM32的通信部分,主要介绍UART(通用异步收发器),是一种异步、全双工的通信方式。 内容         首先,我们先来介绍一下通信的基本知识,之前在51单片机的学习中我们也接触过UART,在此就不做详细介绍,感兴
小点点
2022/12/12
1.8K0
(十三)STM32——串口通信(UART)
【STM32】串口通信---用代码与芯片对话
文章目录 前言 一、串口通信基本知识 二、编程思路 usart.h usart.c main.c 宏定义 三、总结 前言 开发板:stm32f407VET6 开发环境:keil5 MDK 一、串口通信基本知识 【STM32】5分钟了解STM32的串口通信 二、编程思路 usart.h // ============================================= # @Time : 2020-09-03 # @Author : AXYZdong # @CSDN : http
AXYZdong
2020/11/05
8870
基于单片机设计的智慧农业大棚检测系统
本项目基于单片机设计一个智慧农业大棚检测系统,以提供实时监测和管理大棚环境的关键参数。系统支持环境温度、湿度检测,光照强度检测,并能根据预设的阀值进行报警提示。为了实现数据的显示和管理,该系统还利用Qt开发了一款对应的Android手机APP,通过蓝牙传输模块将单片机采集到的数据传递到手机APP上进行显示和管理。
DS小龙哥
2024/01/09
1.4K0
基于单片机设计的智慧农业大棚检测系统
相关推荐
没有串口,如何打印单片机调试信息?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档