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->CR3
),将 GPIOB->OSPEEDR
更改为高速,但仍然没有。我在 x86 笔记本电脑上使用 Manjaro Linux。如果有帮助,我可以提供有关我的笔记本电脑配置的更多背景信息。
我仍然怀疑我没有正确配置 USART1->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 输出的主要内容,如果未能解决你的问题,请参考以下文章
在 Nucleo-F446RE 上通过 CANBUS 进行引导加载程序访问
使用板 Nucleo-f401re 从 Lepton FLIR 相机获取连续流