USART1 没有为 Nucleo F411RE 提供任何 Putty 输出

Posted

技术标签:

【中文标题】USART1 没有为 Nucleo F411RE 提供任何 Putty 输出【英文标题】:USART1 not giving any Putty output for Nucleo F411RE 【发布时间】:2020-12-11 16:04:34 【问题描述】:

供应商:STM32 MC:Nucleo F411RE 相关链接:数据表、参考手册、Nucleo 手册

问题:我正在学习使用 STM32、ARM Cortex M4 处理器的嵌入式裸机。我已经用 Putty 正确配置了 USART2。即使我更改波特率,USART2 的输出也可以正常工作。但是,我根本无法让 USART1 在 Putty 上传输任何内容。

端口:GPIOB 别针:6 APB2 时钟:84MHz 波特率:115200 **USART1_BRR = 84MHz / 115200 = 729 [即0x02D9]

下面是我的时钟配置截图:

这是我的代码:

#include <stm32f4xx.h>

void USART1_Init(void);
void USART1_Write(int ch);
void delayMs(int delay);

int main(void)

    USART1_Init();
    while(1) 
        USART1_Write('K');
        delayMs(100);
    


void USART1_Init(void)

    RCC->AHB1ENR |= 0x0002;
    RCC->APB2ENR |= 0x0010;

    GPIOB->MODER |= 0x2000;
    GPIOB->AFR[0] |= 0x7000000;

    USART1->BRR = 0x02D9;       // 115200 @84MHz
    USART1->CR1 = 0x0008;
    USART1->CR1 |= 0x2000;


void USART1_Write(int ch)

    while (!(USART1->SR & 0x0080)) 
    USART1->DR = (ch & 0xFF);


void delayMs(int delay)

    int i;
    while (delay > 0) 
        for (i = 0; i < 3195; i++) 
        --delay;
    

我做了什么: 我检查了所有配置是否正常工作。以下是 RCC、GPIOB 和 USART1 寄存器的截图:

起初,我尝试使用USART1 的默认引脚(PA9 和 PA10)。但后来,我在某处读到它们可能配置为 USB 输出。所以我打开 PB6 和 PB7 分别用于USART1 TX 和 RX。

我尝试更改波特率,打开 DMAT (USART1-&gt;CR3),将 GPIOB-&gt;OSPEEDR 更改为高速,但仍然没有。我在 x86 笔记本电脑上使用 Manjaro Linux。如果有帮助,我可以提供有关我的笔记本电脑配置的更多背景信息。

我仍然怀疑我没有正确配置 USART1-&gt;BRR,或者将 USART1 作为备用功能打开需要比现在更多。

我仍然是嵌入式的初学者,我尝试了从框图和参考手册中可以推断出的一切。但我似乎根本无法让它发挥作用。 STM32 上的 USART1 是否还需要我做更多事情才能使其正常工作?

【问题讨论】:

115200 @84MHz 您在代码的哪个位置将时钟设置为 84MHz? 我在连接到调试端口的 pa2/3 上使用 usart2。无需使用额外的电线。 您只为两个引脚之一设置了调制解调器,并且您没有事先或同时将位归零。 @old_timer 我使用 CubeMX 设置来配置时钟(它是默认的 tbh)。检查第一个屏幕截图。 所以你正在加载到 ram 中运行它? 【参考方案1】:

一个完整的例子;无需其他代码。

flash.s

.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
    ldr r0,stacktop
    mov sp,r0
    bl notmain
    b hang
.thumb_func
hang:   b .

.align
stacktop: .word 0x20001000

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.thumb_func
.globl dummy
dummy:
    bx lr

flash.ld

MEMORY

    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000

SECTIONS

    .text   :  *(.text*)    > rom
    .rodata :  *(.rodata*)  > rom
    .bss    :  *(.bss*)     > ram

notmain.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );

#define RCCBASE 0x40023800
#define RCC_CR          (RCCBASE+0x00)
#define RCC_CFGR        (RCCBASE+0x08)
#define RCC_APB1RSTR    (RCCBASE+0x20)
#define RCC_AHB1ENR     (RCCBASE+0x30)
#define RCC_APB1ENR     (RCCBASE+0x40)

#define GPIOABASE 0x40020000
#define GPIOA_MODER     (GPIOABASE+0x00)
#define GPIOA_AFRL      (GPIOABASE+0x20)

#define USART2BASE 0x40004400
#define USART2_SR       (USART2BASE+0x00)
#define USART2_DR       (USART2BASE+0x04)
#define USART2_BRR      (USART2BASE+0x08)
#define USART2_CR1      (USART2BASE+0x0C)

//PA2 is USART2_TX alternate function 1
//PA3 is USART2_RX alternate function 1

static int clock_init ( void )

    unsigned int ra;

    //switch to external clock.
    ra=GET32(RCC_CR);
    ra|=1<<16;
    PUT32(RCC_CR,ra);
    while(1) if(GET32(RCC_CR)&(1<<17)) break;
    ra=GET32(RCC_CFGR);
    ra&=~3;
    ra|=1;
    PUT32(RCC_CFGR,ra);
    while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;

    return(0);

int uart2_init ( void )

    unsigned int ra;

    ra=GET32(RCC_AHB1ENR);
    ra|=1<<0; //enable port A
    PUT32(RCC_AHB1ENR,ra);

    ra=GET32(RCC_APB1ENR);
    ra|=1<<17; //enable USART2
    PUT32(RCC_APB1ENR,ra);

    ra=GET32(GPIOA_MODER);
    ra&=~(3<<4); //PA2
    ra&=~(3<<6); //PA3
    ra|=2<<4; //PA2
    ra|=2<<6; //PA3
    PUT32(GPIOA_MODER,ra);

    ra=GET32(GPIOA_AFRL);
    ra&=~(0xF<<8); //PA2
    ra&=~(0xF<<12); //PA3
    ra|=0x7<<8; //PA2
    ra|=0x7<<12; //PA3
    PUT32(GPIOA_AFRL,ra);

    ra=GET32(RCC_APB1RSTR);
    ra|=1<<17; //reset USART2
    PUT32(RCC_APB1RSTR,ra);
    ra&=~(1<<17);
    PUT32(RCC_APB1RSTR,ra);

    //8000000/(16*115200) = 4.34  4+5/16
    PUT32(USART2_BRR,0x45);
    PUT32(USART2_CR1,(1<<3)|(1<<2)|(1<<13));

    return(0);


void uart2_send ( unsigned int x )

    while(1) if(GET32(USART2_SR)&(1<<7)) break;
    PUT32(USART2_DR,x);


int notmain ( void )

    unsigned int rx;

    clock_init();
    uart2_init();
    for(rx=0;;rx++)
    
        uart2_send(0x30+(rx&7));
    
    return(0);

构建

arm-linux-gnueabi-as --warn --fatal-warnings -mcpu=cortex-m4 flash.s -o flash.o
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m4 -mthumb -c notmain.c -o notmain.o
arm-linux-gnueabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-linux-gnueabi-objdump -D notmain.elf > notmain.list
arm-linux-gnueabi-objcopy -O binary notmain.elf notmain.bin

arm-whatever-whatever will work...

检查文件

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000011    stmdaeq r0, r0, r4
 8000008:   08000017    stmdaeq r0, r0, r1, r2, r4
 800000c:   08000017    stmdaeq r0, r0, r1, r2, r4

08000010 <reset>:
 8000010:   f000 f86e   bl  80000f0 <notmain>
 8000014:   e7ff        b.n 8000016 <hang>

08000016 <hang>:
 8000016:   e7fe        b.n 8000016 <hang>

向量表看起来不错,有一半的机会可以启动。

将 notmain.bin 复制到插入卡时创建的虚拟驱动器。

它将永远在由板子调试器端创建的虚拟 COM 端口 (115200 8N1) 上爆炸 0123456701234567。

并不是我使用如图所示的 rx,但您似乎只设置了两者之一。

我没有看到您在设置现代寄存器位之前将它们归零。

除非您在其他地方神奇地设置时钟然后在事后运行此代码(而不是正常开机/重置),否则波特率寄存器的数学运算看起来是错误的。

afrl 也是如此;我今天没有查看寄存器,但是任何时候您更改位(不仅仅是将一位设置为一位),您都需要将字段中的其他位归零。在您的情况下,7 可能是所有位,因此 an 或 equals 可能会起作用,但请检查一下。

我建议您在一个寄存器写入中执行此操作,而不是使用魔术易失性指针 &= 然后在单独的步骤中执行 |=。相反 x = register; x&=....x|=.. 然后寄存器=x;该功能不会更改模式两次,只会更改一次。取决于功能和外围设备以及它对写入的反应如何改变两次是否可以(在这种情况下可能很好,一般规则)。

如果您正在为时钟做一些其他魔术,他们也可能会弄乱 uart,最好重置它,通常对于像这样的外围设备(甚至可能已经在文档中,没有查看尽管)。否则,您将不处于已知状态(如果您正在预运行其他内容,则程序中的任何内容都是如此)并且您不能简单地触摸几个寄存器中的几个字段,而必须触摸整个外围设备。

我认为不需要上面的时钟初始化,我只是将其切换为使用基于晶体的时钟而不是片上 RC 时钟。

编辑

非常抱歉,重新阅读您的问题。即使它不是您所要求的,仍按原样保留在上方,因此此修改使 uart 使用 UART1_TX 在 PA9 上发送。

notmain.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );

#define RCCBASE 0x40023800
#define RCC_CR          (RCCBASE+0x00)
#define RCC_CFGR        (RCCBASE+0x08)
//#define RCC_APB1RSTR    (RCCBASE+0x20)
#define RCC_APB2RSTR    (RCCBASE+0x24)
#define RCC_AHB1ENR     (RCCBASE+0x30)
//#define RCC_APB1ENR     (RCCBASE+0x40)
#define RCC_APB2ENR     (RCCBASE+0x44)

#define GPIOABASE 0x40020000
#define GPIOA_MODER     (GPIOABASE+0x00)
//#define GPIOA_AFRL      (GPIOABASE+0x20)
#define GPIOA_AFRH      (GPIOABASE+0x24)

#define USART1BASE 0x40011000
#define USART1_SR       (USART1BASE+0x00)
#define USART1_DR       (USART1BASE+0x04)
#define USART1_BRR      (USART1BASE+0x08)
#define USART1_CR1      (USART1BASE+0x0C)

//PA9 is USART1_TX alternate function 7

static int clock_init ( void )

    unsigned int ra;

    //switch to external clock.
    ra=GET32(RCC_CR);
    ra|=1<<16;
    PUT32(RCC_CR,ra);
    while(1) if(GET32(RCC_CR)&(1<<17)) break;
    ra=GET32(RCC_CFGR);
    ra&=~3;
    ra|=1;
    PUT32(RCC_CFGR,ra);
    while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;

    return(0);

int uart_init ( void )

    unsigned int ra;

    ra=GET32(RCC_AHB1ENR);
    ra|=1<<0; //enable port A
    PUT32(RCC_AHB1ENR,ra);

    ra=GET32(RCC_APB2ENR);
    ra|=1<<4; //enable USART1
    PUT32(RCC_APB2ENR,ra);

    ra=GET32(GPIOA_MODER);
    ra&=~(3<<(9<<1)); //PA9
    ra|=  2<<(9<<1) ; //PA9
    PUT32(GPIOA_MODER,ra);

    ra=GET32(GPIOA_AFRH);
    ra&=~(0xF<<4); //PA9
    ra|=  0x7<<4; //PA9
    PUT32(GPIOA_AFRH,ra);

    ra=GET32(RCC_APB2RSTR);
    ra|=1<<4; //reset USART1
    PUT32(RCC_APB2RSTR,ra);
    ra&=~(1<<4);
    PUT32(RCC_APB2RSTR,ra);

    //8000000/(16*115200) = 4.34  4+5/16
    PUT32(USART1_BRR,0x45);
    PUT32(USART1_CR1,(1<<3)|(1<<2)|(1<<13));

    return(0);


void uart_send ( unsigned int x )

    while(1) if(GET32(USART1_SR)&(1<<7)) break;
    PUT32(USART1_DR,x);


int notmain ( void )

    unsigned int rx;

    clock_init();
    uart_init();
    for(rx=0;;rx++)
    
        uart_send(0x30+(rx&7));
    
    return(0);

PA9 绑定到外部接头引脚,Arduino 风格的数据引脚,他们不太可能也将其用于 USB。

MODER 将这些引脚重置为零,因此 an 或 equal 将起作用。

AFRL 和 AFRH 重置为零,因此 an 或 equal 将起作用。

要查看输出需要将UART设备连接到PA9,如果要查看UART1工作,数据不会通过虚拟com端口。

我将时钟从 16Mhz 更改为 8MHz,所以对于这个芯片的 uart(ST 在他们的库中有不同的外围设备,当他们制造芯片时可以从中挑选)

    //8000000/(16*115200) = 4.34  4+5/16
    PUT32(USART1_BRR,0x45);

如果您考虑一下 8000000/115200 = 69.444 = 0x45。您无需单独进行分数数学运算。

因此,查看您的代码,您正在执行 PB6,这对于 USART1_TX 备用函数 7 来说很好。除了 BRR 并且您的延迟函数可能是死代码并优化之外,一切看起来都很好,但是因为您正在寻找 tx 空状态在添加一个应该允许您的代码工作的字符之前先一点。

PB6 是插头引脚之一,因此您可以将 (3.3v) UART 连接到它并查看您的数据是否输出。如果没有别的,我建议您在 BRR 中尝试 16000000/115200 = 138.8 = 0x8A 或 0x8B,为什么不只花一秒钟。

否则,如果您有示波器,请在此处放置探头。我建议使用 U 而不是字母 K,它是 0x55,当您尽可能快地传输(字符之间没有间隙)并且非常容易在示波器上测量时,它与 8N1 一起作为方波出现。然后弄乱你的 BRR 寄存器,看看它如何改变示波器上的输出频率。

这在 PB6 上使用 USART1_TX,并且我移除了晶体时钟初始化,因此它使用 16MHz HSI 时钟。

从根本上说,这里的区别在于您有不同的 BRR 设置。

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );

#define RCCBASE 0x40023800
#define RCC_CR          (RCCBASE+0x00)
#define RCC_CFGR        (RCCBASE+0x08)
//#define RCC_APB1RSTR    (RCCBASE+0x20)
#define RCC_APB2RSTR    (RCCBASE+0x24)
#define RCC_AHB1ENR     (RCCBASE+0x30)
//#define RCC_APB1ENR     (RCCBASE+0x40)
#define RCC_APB2ENR     (RCCBASE+0x44)

#define GPIOBBASE 0x40020400
#define GPIOB_MODER     (GPIOBBASE+0x00)
#define GPIOB_AFRL      (GPIOBBASE+0x20)

#define USART1BASE 0x40011000
#define USART1_SR       (USART1BASE+0x00)
#define USART1_DR       (USART1BASE+0x04)
#define USART1_BRR      (USART1BASE+0x08)
#define USART1_CR1      (USART1BASE+0x0C)

int uart_init ( void )

    unsigned int ra;

    ra=GET32(RCC_AHB1ENR);
    ra|=1<<1; //enable port B
    PUT32(RCC_AHB1ENR,ra);

    ra=GET32(RCC_APB2ENR);
    ra|=1<<4; //enable USART1
    PUT32(RCC_APB2ENR,ra);

    ra=GET32(GPIOB_MODER);
    ra&=~(3<<(6<<1)); //PB6
    ra|=  2<<(6<<1) ; //PB6
    PUT32(GPIOB_MODER,ra);

    ra=GET32(GPIOB_AFRL);
    ra&=~(0xF<<24); //PB6
    ra|=  0x7<<24; //PB6
    PUT32(GPIOB_AFRL,ra);

    ra=GET32(RCC_APB2RSTR);
    ra|=1<<4; //reset USART1
    PUT32(RCC_APB2RSTR,ra);
    ra&=~(1<<4);
    PUT32(RCC_APB2RSTR,ra);

    //16000000/115200
    PUT32(USART1_BRR,0x8B);
    PUT32(USART1_CR1,(1<<3)|(1<<13));

    return(0);


void uart_send ( unsigned int x )

    while(1) if(GET32(USART1_SR)&(1<<7)) break;
    PUT32(USART1_DR,x);


int notmain ( void )

    unsigned int rx;

    uart_init();
    for(rx=0;;rx++)
    
        uart_send(0x30+(rx&7));
    
    return(0);

另请注意,以这种速率进行爆破时,根据数据模式,接收器可能会不同步,从而接收到的字符不是发送的字符,因此您可能需要按住重置按钮板,然后释放并查看接收器是否看到所需的模式,也许这就是为什么你要爆破 K 而不是 U 或其他东西。

PB6 引脚是板 D10 而不是 D8 右侧 PA9 引脚上方的两个引脚,请注意右侧的公引脚比母 arduino 插头引脚低半步,查看文档板找出在哪里连接你的 uart。

【讨论】:

我更新了 MODER 以同时拥有 PB6 和 PB7 引脚我将 |= 更改为 = 以将其他位设置为零(AFR、MODER、GPIO)你能告诉我在哪里可以找到确切的时钟吗计算我的波特率?屏幕截图的频率为 84MHz - 但这可能是错误的。当我将 9600 与 USART2 一起使用时,它工作正常。我还能够将其转换为 115200 并且效果很好。 除基地址外,此应用的 uart1 和 uart2 之间的 uart 配置相同。如果您更改其他内容,例如 uart 工作的外围时钟,那么是的 brr 会更改。 我的猜测是,您正在使用 putty 查看连接板时显示的虚拟 com 端口,而不是使用额外的解决方案,例如带有线到引脚的 usb/uart 板董事会?如原理图所示,只有 PA2/3 连接到板上的调试器,这是虚拟 com 端口的来源,因此只有连接到该调试器的 uart 才能工作,您可以在数据表中看到只有 uart2 可以复用到 pa2/ 3.因此,如果您将 putty 连接到,则只能使用 uart2。 为迟到的回复道歉。我也尝试了 BRR = 0x8A 和 BRR = 0x8B,但它没有用。由于我是初学者,因此我正在尝试查看是否还有其他配置我错过了。我可能需要了解更多关于 BUS 和 GPIO 端口的时钟速度等信息。 你是如何连接到 uart 的?你有 ftdi 分线板什么的吗?你选对了吗?

以上是关于USART1 没有为 Nucleo F411RE 提供任何 Putty 输出的主要内容,如果未能解决你的问题,请参考以下文章

STM32F411RE:程序冻结,超声波传感器不工作

STM32F411RE和L610物联网入门学习笔记

在 Nucleo-F446RE 上通过 CANBUS 进行引导加载程序访问

使用板 Nucleo-f401re 从 Lepton FLIR 相机获取连续流

为啥 Nucleo 144 上的引脚没有输出足够高的电压,尽管输出设置为 HIGH?

在STM32 Nucleo上多次触发上升沿中断