如何解决USART1没有为Nucleo F411RE提供任何腻子输出
供应商:STM32 MC :Nucleo F411RE 相关链接:数据表,参考手册,Nucleo手册
问题:我正在使用STM32,ARM Cortex M4处理器学习嵌入式裸机。我已经用腻子正确配置了USART2。即使更改波特率,USART2的输出也可以正常工作。但是,我根本无法让USART1在Putty上传输任何内容。
端口:GPIOB
Pin :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做更多的事情吗?
解决方法
一个完整的例子;不需要其他代码。
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
手臂-任何东西-都可以工作...
检查文件
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1,r0,r0
8000004: 08000011 stmdaeq r0,{r0,r4}
8000008: 08000017 stmdaeq r0,r1,r2,r4}
800000c: 08000017 stmdaeq r0,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复制到插入卡时创建的虚拟驱动器。
它将在板的调试器端(115200 8N1)创建的虚拟com端口上永久爆炸0123456701234567。
并不是说我正在使用rx,但是您似乎只设置了两者之一。
我看不到在设置调制解调器寄存器位之前将它们清零了。
除非您在其他地方神奇地设置了时钟,然后在事后运行此代码(而不是正常的上电/复位),否则数学运算在波特率寄存器上看起来是错误的。
同样适用于非洲;我今天没有看寄存器,但是每当您更改位(不仅将一位设置为1)时,都需要将字段中的其他位清零。在您的情况下,可能所有位都是7,所以可以等于或相等,但请检查一下。
我建议您在一次寄存器写入中执行此操作,而不是在魔术易失指针&=中进行操作,然后在单独的步骤| =中进行操作。取而代之的是x =寄存器; x&= .... x | = ....然后寄存器= x;该功能不会更改模式两次,而只会更改一次。取决于功能和外围设备及其对写入的反应,是否可以进行两次更改(在这种情况下,一般规则可能很好)。
如果您正在为时钟做其他魔术操作,则它们也可能与uart混为一谈,最好将其重置,通常对于这样的外围设备(可能甚至在文档中都没有看过,而)。否则,您就不会处于已知状态(如果您正在运行其他程序,那么该程序中的任何内容都是正确的),并且您不能简单地触摸几个寄存器中的几个字段,而必须触摸整个外围设备。
我认为不需要上面的时钟init,我只是将其切换为使用基于晶体的时钟,而不是片上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_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重置为零,因此等于或等于工作。
AFRL和AFRH重置为零,因此等于或相等将起作用。
要查看输出,您需要将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,那为什么不花一秒钟呢?
否则,如果您有示波器,请在上面放一个探头。我建议不要使用字母K,而是使用0x55的U,它与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(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);
//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右侧的PA9引脚上方的两个引脚,而不是D8,请注意,右侧的公引脚比arduino母头引脚的引脚低半个台阶,请参阅有关文档。板上找到将uart连接起来的地方。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。