USART1 не выдает вывод Putty для Nucleo F411RE

Производитель: STM32 MC: Nucleo F411RE Соответствующие ссылки: лист данных, справочное руководство, руководство Nucleo

Проблема: я изучаю встраиваемое «голое железо» с помощью процессора STM32, ARM Cortex M4. Я правильно настроил USART2 с Putty. Выходной сигнал USART2 работает нормально, даже если я изменяю скорость передачи данных. Однако я вообще не могу заставить USART1 передавать что-либо на Putty.

Порт: GPIOB Контакт: 6 Часы APB2: 84 МГц Скорость передачи: 115200 ** USART1_BRR = 84 МГц / 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:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Сначала я попытался использовать контакты по умолчанию (PA9 и PA10) для USART1. Но потом я где-то читал, что они могут быть настроены для вывода USB. Поэтому я включил PB6 и PB7, чтобы использовать их для USART1 TX и RX соответственно.

Я попытался изменить скорость передачи, включить DMAT (USART1->CR3), изменить GPIOB->OSPEEDR на высокую скорость, но все равно ничего. Я использую Manjaro Linux на ноутбуке x86. Если это поможет, я могу предоставить больше информации о конфигурации моего ноутбука.

Я все еще подозреваю, что я неправильно конфигурирую USART1->BRR или включение USART1 в качестве альтернативной функции требует немного больше, чем это уже есть.

Я все еще новичок во встраиваемых системах и пробовал все, что мог сделать из блок-схемы и справочных руководств. Но я вообще не могу заставить это работать. Есть ли что-то еще, что я должен сделать с USART1 на STM32, чтобы это работало?


person That Developer    schedule 22.08.2020    source источник
comment
115200 @ 84MHz, где в вашем коде вы установили частоту 84MHz?   -  person old_timer    schedule 22.08.2020
comment
Я использую usart2 на pa2 / 3, который подключен к порту отладки. никаких дополнительных проводов для использования.   -  person old_timer    schedule 22.08.2020
comment
вы установили модер только для одного из двух выводов, и вы не обнулили биты до руки или в одно и то же время.   -  person old_timer    schedule 22.08.2020
comment
@old_timer Я использовал настройку CubeMX для настройки часов (по умолчанию tbh). Проверьте первый снимок экрана.   -  person That Developer    schedule 22.08.2020
comment
так ты загружаешься в барана, чтобы запустить это?   -  person old_timer    schedule 22.08.2020
comment
@old_timer, если я использую только TX, а не RX, мне все равно нужно активировать контакт RX? Я использовал только TX, поэтому я активировал контакт TX для MODER   -  person That Developer    schedule 22.08.2020
comment
@old_timer Я не понимаю, что вы имеете в виду под загрузкой в ​​оперативную память.   -  person That Developer    schedule 22.08.2020
comment
какое дополнительное оборудование вы используете для подключения putty к usart1?   -  person old_timer    schedule 23.08.2020


Ответы (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

рука-все-что угодно будет работать ...

проверить файл

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 на виртуальный диск, созданный при подключении карты.

Он навсегда загрузит 0123456701234567 на виртуальный COM-порт, созданный отладчиком на плате (115200 8N1).

Не то чтобы я использую rx, как показано, но вы, кажется, установили только один из двух.

Я не вижу, чтобы вы обнуляли биты регистра модера перед их установкой.

Математические расчеты в регистре скорости передачи данных выглядят неверно, если только вы не волшебным образом устанавливаете часы в другом месте, а затем запускаете этот код постфактум (а не при обычном включении / сбросе).

То же самое и с afrl; Сегодня я не смотрел регистр, но каждый раз, когда вы меняете биты (а не просто устанавливаете один бит на единицу), вам нужно обнулить другие биты в поле. В вашем случае 7 может быть всеми битами, поэтому может работать или равно, но проверьте это.

Я бы порекомендовал вам сделать это при записи в один регистр, а не с помощью волшебного изменчивого указателя & =, а затем на отдельном шаге | =. Вместо этого x = register; x & = .... x | = .... тогда register = x; Функция не меняет режим дважды, а только один раз. Зависит от функции и периферийного устройства, а также от того, как оно реагирует на записи относительно того, можно ли изменить дважды (возможно, в этом случае это нормально, общее правило).

Если вы делаете какую-то другую магию для часов, они также могут возиться с uart, неплохо просто сбросить его, в общем для такого периферийного устройства (возможно, даже было в документации, давно не просматривали). В противном случае вы не находитесь в известном состоянии (верно для всего в вашей программе, если вы предварительно запускаете что-то еще), и вы не можете просто коснуться нескольких полей в нескольких регистрах, вы должны коснуться всего периферийного устройства.

Я не думаю, что инициализация часов, указанная выше, требуется, я просто переключил ее на использование кварцевых часов, а не встроенных часов RC.

Редактировать

Очень извините, перечитываю ваш вопрос. Слева вверху как есть, хотя это не то, что вы просили, так что эта модификация делает так, что uart отправляется на PA9 с использованием UART1_TX.

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 сбрасывает эти контакты на ноль, поэтому будет работать равное или равное.

AFRL и AFRH сбрасываются в ноль, поэтому будет работать равный или равный.

Чтобы увидеть вывод, вам нужно подключить устройство uart к PA9, данные не проходят через виртуальный com-порт, если вы хотите увидеть работу UART1.

Я изменил частоту с 16 МГц на 8 МГц, поэтому для 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 - это один из выводов заголовка, поэтому вы можете подключить к нему UART (3,3 В) и посмотреть, поступают ли ваши данные. Я бы порекомендовал вам просто попробовать 16000000/115200 = 138,8 = 0x8A или 0x8B в BRR, почему бы не всего за секунду.

В противном случае, если у вас есть прицел, поместите туда зонд. Я рекомендую вместо буквы K использовать U, который равен 0x55, что с 8N1, которое выглядит как прямоугольная волна, когда вы передаете так быстро, как можете (без пробелов между символами), и его действительно легко измерить с помощью осциллографа. затем поиграйте со своим регистром BRR и посмотрите, как это изменит частоту выходного сигнала осциллографа.

Это использует USART1_TX на PB6, и я удалил инициализацию кварцевых часов, поэтому он использует тактовую частоту HSI 16 МГц.

Принципиальная разница здесь в том, что у вас другая настройка 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 на два вывода выше вывода PA9 на правой стороне платы D10 вместо D8, обратите внимание, что штыревые контакты справа находятся на полшага ниже штырьков заголовка Arduino, посмотрите документацию к плате, чтобы найти где подключить свой uart.

person old_timer    schedule 22.08.2020
comment
Я обновил MODER, чтобы у меня были контакты PB6 и PB7, которые я изменил | = на =, где это необходимо, чтобы установить другие биты в ноль (AFR, MODER, GPIO). Вы можете посоветовать, где я могу найти точные часы для расчета моей скорости передачи данных? На скриншоте это 84 МГц, но это может быть неверно. Когда я использовал 9600 с USART2, он работал нормально. Я также смог преобразовать его в 115200, и он работал нормально. - person That Developer; 22.08.2020
comment
кроме базового адреса, uart настроен одинаково между uart1 и uart2 для этого приложения. Если вы измените другие вещи, например, периферийные часы, от которых работает uart, тогда да, брр изменится. - person old_timer; 23.08.2020
comment
Я предполагаю, что вы используете замазку, чтобы посмотреть на виртуальный COM-порт, который появляется, когда плата подключена, и не используете дополнительное решение, такое как плата usb / uart с проводом к контакту на плате? Как показано на схемах, только PA2 / 3 подключен к отладчику на плате, откуда исходит виртуальный COM-порт, поэтому будут работать только подключенные к нему uarts, что вы можете видеть в таблице данных, только uart2 может быть мультиплексирован на pa2 / 3. поэтому можно использовать только uart2, если это то, к чему вы подключаете шпатлевку. - person old_timer; 23.08.2020
comment
извиняюсь за поздний ответ. Я также пробовал с BRR = 0x8A и BRR = 0x8B, но это не сработало. Поскольку я новичок, я пытаюсь увидеть, есть ли еще какие-то конфигурации, которые я пропустил. Мне, вероятно, нужно больше узнать о тактовой частоте и т. Д. Для портов BUS и GPIO. - person That Developer; 27.08.2020
comment
как ты к uart подключаешься? у вас есть коммутационная плата ftdi или что-то в этом роде? ты на правильном булавке? - person old_timer; 27.08.2020