Как стать автором
Обновить

Обзор платы Black Pill V2.0

Уровень сложностиПростой
Время на прочтение3 мин
Количество просмотров10K
Всего голосов 8: ↑7 и ↓1+10
Комментарии18

Комментарии 18

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

Но если вспомнить, что микроконтроллеры с периферией работают, через 2 наносекунды после начала работы, у вас из nucleo получится такой же паук, из которого провода в датчики, экраны и кнопки уйдут.

Ну и если stlink соединять шлейфом, как и задумывалось, а не по проводку, тоже будет выглядеть всё приличнее.

А если взять daplink или ещё какой-нибудь дебаггер, который умеет в cmsis-dap, то из компа будет выходить один провод и из отладчика несколько проводов в плату, и всё

А если не жульничать, то и питание тоже с платы отладчика можно подавать, т.к. в nucleo все через один провод тоже идёт

В общем, не понятно почему у вас эти проблемы. Это не обзор black pill, а какая-то жалоба на то, что вы с проводами не можете справиться

Если уж используем копеечную таблетку, то к ней хорошо идет китайский же копеечный ST-LINK, где выведено 5В и 3.3В, причем некоторые можно перешить в версию 2.1, где уже есть CDC UART, или взять чуть более дорогой китайский JLINK-OB, где UART уже есть. При этом от таблетки будет идти всего один шлейфик с питанием, отладкой и консолью.

А ещё таблетку и саму можно превратить в Black Magic Probe.

Причём просто для заливки прошивки теоретически ничего, кроме USB-кабеля, вообще не нужно, поскольку, в отличие от STM32F103, в чипах F401/F411 есть встроенный загрузчик с поддержкой USB DFU. Правда, как обычно, гладко было на бумаге:

  • В загрузчике F401/F411 есть особенность, больше похожая на баг — не включается внутренняя подтяжка для вывода PA10, который используется как USART1 RX. В результате, если к этому пину ничего не подключено, загрузчик может поймать там мусор от наводок, после чего уйдёт в режим работы через последовательный порт и перестанет работать с USB. Поэтому в любом устройстве, где предполагается использовать встроенный загрузчик F4x1, необходимо каким-то способом обеспечить стабильный уровень на входе PA10 во время работы загрузчика (например, подключить туда внешний резистор подтяжки).

  • По каким-то причинам частота кварца на платах Blackpill — 25 МГц, а не более обычные для отладочных плат на STM32 8 МГц. Для работы прошивки, где параметры тактирования указываются статически при сборке, это не имеет особого значения, а вот загрузчик пытается определить частоту кварца путём сравнения её с частотой внутреннего RC-генератора (HSI), и запас на неточность частоты HSI при необходимости различить 24/25/26 МГц значительно меньше, чем при различении 7/8/9 МГц. В результате этого в описании платы всерьёз предлагается в случае проблем с запуском загрузчика обеспечить температуру чипа 25°C.

  • Для входа в загрузчик на входе PB2 в момент сброса должен быть уровень 0; на плате Blackpill это реализовано путём соединения этого пина с GND через резистор, который может мешать в каких-то вариантах применения этого пина.

  • Наконец, для входа в загрузчик нужно установить на входе BOOT0 уровень 1, что на этой плате может быть сделано единственным образом — путём нажатия кнопки BOOT; больше никуда сигнал BOOT0 не выведен. Можно, конечно, реализовать вход в загрузчик как одну из функций собственной прошивки, а вариант с доступом к кнопке BOOT оставить в качестве средства восстановления после неудачной прошивки, смирившись с необходимостью разбирать устройство для этой цели.

Это неудобно, нажимать BOOT, RESET, ждать переинициализации USB. И все равно желателен UART для консоли. Гораздо удобнее, когда просто даешь команду Prog и через несколько секунд процессор уже сам запускается с новой прошивкой.

Это неудобно, нажимать BOOT, RESET, ждать переинициализации USB. И все равно желателен UART для консоли.

Как раз сейчас пишу статью по превращению stm32f103 в такой вот UART программатор-отладчик.


Кстати! Для полноты картины не могли бы вы подробно описать процесс прошивки по UART из windows, как там из консоли менять параметры порта (скорость, четность) и можно ли порту дать осмысленное имя?

не могли бы вы подробно описать процесс прошивки по UART из windows, как там из консоли менять параметры порта (скорость, четность) и можно ли порту дать осмысленное имя?




https://www.xanthium.in/Serial-Port-Programming-using-Win32-API

не могли бы вы подробно описать процесс прошивки по UART из windows, как там из консоли менять параметры порта (скорость, четность) и можно ли порту дать осмысленное имя?

#ifndef SCAN_SERIAL_PORT_H
#define SCAN_SERIAL_PORT_H

// https://www.xanthium.in/Serial-Port-Programming-using-Win32-API

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stdint.h>
#include <windows.h>

#define DEDUG_RX_TEXT 0
#define MAX_COM_NUM 20
#define DEBUG_FINE_VI_REQ 0
#define DEBUG_SPOT_COM 0
#define DEBUG_FAILED_OPENS 0
#define MAX_NUM_COM_DEV 40U


typedef struct  {
    bool isExistPort;

} xSerialConnection_t;

extern xSerialConnection_t deviceList[MAX_COM_NUM];
extern HANDLE hComm;

bool com_receive_remain(HANDLE hComm, uint32_t* outRealRxArrayLen);
bool init_serial(char* com_name, uint32_t baud_rate);
bool scan_serial(void);
bool com_send_str(HANDLE hComm, char* txBuffer, uint32_t txBuffLen);
bool com_receive_str(HANDLE hComm, char* outRxArray, uint32_t capasityRxArray, uint32_t* outRealRxArrayLen);
bool com_receive_str_timeout(HANDLE hComm, char* outRxArray, uint32_t capasityRxArray, uint32_t* len, uint32_t time_out_ms);

bool serial_init(void);

#ifdef __cplusplus
}
#endif

#endif /* SCAN_SERIAL_PORT_H */


---------------------------------

#include "scan_serial_port.h"

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

#include "log.h"
#include "str_utils.h"
#include "win_utils.h"

#ifdef HAS_MCU
#error That code only for desktop builds
#endif

HANDLE hComm;

xSerialConnection_t deviceList[MAX_COM_NUM];

static bool com_set_params(HANDLE hComm, uint32_t baud_rate) {
    bool res = false;
    // Initializing DCB structure
    DCB dcbSerialParams = {0};
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

    BOOL status = GetCommState(hComm, &dcbSerialParams);
    dcbSerialParams.BaudRate = baud_rate;  // Setting BaudRate = 460800
    dcbSerialParams.ByteSize = 8;          // Setting ByteSize = 8
    dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
    dcbSerialParams.Parity = NOPARITY;     // Setting Parity = None

    SetCommState(hComm, &dcbSerialParams);
    if(status) {
        res = true;
    }
    return res;
}

static bool com_set_timeout(HANDLE hComm) {
    bool res = false;
    COMMTIMEOUTS timeouts = {0};
    timeouts.ReadIntervalTimeout = 2;        // in milliseconds
    timeouts.ReadTotalTimeoutMultiplier = 5; //
    timeouts.ReadTotalTimeoutConstant = 100; // in milliseconds
    timeouts.WriteTotalTimeoutConstant = 1;  // in milliseconds
    timeouts.WriteTotalTimeoutMultiplier = 1;

    SetCommTimeouts(hComm, &timeouts);
    return res;
}

bool com_send_str(HANDLE hComm, char* txBuffer, uint32_t tx_buff_len) {
    LOG_DEBUG(COM,"COMsend [%s]", txBuffer);
    bool res = false;
    uint32_t remain_len = 0;
    res = com_receive_remain(hComm, &remain_len);
    BOOL status;
    DWORD dNoOfBytesWritten = 0;
    status = WriteFile(hComm, txBuffer, (DWORD)tx_buff_len, &dNoOfBytesWritten, NULL);
    if(dNoOfBytesWritten == tx_buff_len) {
        if(status) {
            res = true;
        }
    } else {
        LOG_ERROR(COM,"WriteSerialErr [%s]",txBuffer);
    }
    LOG_DEBUG(COM,"COMsend end");
    return res;
}

bool com_receive_remain(HANDLE hComm, uint32_t* remain_len) {
    bool res = false;
#ifdef HAS_COM_PORT_DEBUG
    LOG_DEBUG(COM,"%s():", __FUNCTION__);
#endif
    if(NULL != remain_len) {
        *remain_len = 0;
        char tempChar;
        DWORD numberBytesRead;
        uint32_t BytesReadCnt = 0;
        bool loopRun = true;
        while(loopRun) {
            ReadFile(hComm,            // Handle of the Serial port
                     &tempChar,        // Temporary character
                     sizeof(tempChar), // Size of TempChar
                     &numberBytesRead, // Number of bytes read
                     NULL);
#if DEDUG_RX_CHAR
            LOG_DEBUG(COM,"%c", tempChar);
#endif
            if(0 < numberBytesRead) {
                loopRun = true;
            } else {
                loopRun = false;
            }
            BytesReadCnt++;
        };
        if(0 < BytesReadCnt) {
            *remain_len = BytesReadCnt;
            res = true;
        }
    }
    return res;
}

bool com_receive_str(HANDLE hComm, char* outRxArray, uint32_t capasityRxArray, uint32_t* remain_len) {
    bool res = false;
#ifdef HAS_COM_PORT_DEBUG
    LOG_DEBUG(COM,"%s():", __FUNCTION__);
#endif
    if((0 < capasityRxArray) && (NULL != outRxArray)) {
        *remain_len = 0;
        char tempChar;
        DWORD numberBytesRead;
        uint32_t BytesReadCnt = 0;
        bool loopRun = true;
        uint32_t  ret= 0;
        while(loopRun) {
            ret = ReadFile(hComm,            // Handle of the Serial port
                     &tempChar,        // Temporary character
                     sizeof(tempChar), // Size of TempChar
                     &numberBytesRead, // Number of bytes read
                     NULL);
            if(0==ret){
                loopRun = false;
            }
#ifdef DEDUG_RX_CHAR
            LOG_DEBUG(COM,"%c", tempChar);
#endif
            if(0 < numberBytesRead) {
                if(BytesReadCnt < capasityRxArray) {
                    outRxArray[BytesReadCnt] = tempChar; // Store Tempchar into buffer
                }
            } else {
                loopRun = false;
            }
            BytesReadCnt++;
        };
        if(0 < BytesReadCnt) {
            *remain_len = BytesReadCnt;
            res = true;
        }
    }
#ifdef HAS_COM_PORT_DEBUG
    LOG_DEBUG(COM,"%s(): end", __FUNCTION__);
#endif
    return res;
}

bool com_receive_str_timeout(HANDLE hComm, char* outRxArray, uint32_t capasityRxArray, uint32_t* remain_len, uint32_t time_out_ms) {
    bool res = false;
#ifdef HAS_COM_PORT_DEBUG
    LOG_DEBUG(COM,"%s():", __FUNCTION__);
#endif
    if((0 < capasityRxArray) && (NULL != outRxArray)) {
        *remain_len = 0;
        char tempChar;
        DWORD numberBytesRead =0;
        uint32_t BytesReadCnt = 0;
        bool loopRun = true;
        uint32_t  ret= 0;
        struct timeval start, cur;
        double diff_secs = 0;
        gettimeofday(&start, NULL);
        while(loopRun) {
            gettimeofday(&cur, NULL);
            diff_secs = (double)(cur.tv_usec - start.tv_usec) / 1000000 + (double)(cur.tv_sec - start.tv_sec);
            if(time_out_ms<diff_secs*1000 ){
                loopRun = false;
            }
            ret = ReadFile(hComm,            // Handle of the Serial port
                     &tempChar,        // Temporary character
                     sizeof(tempChar), // Size of TempChar
                     &numberBytesRead, // Number of bytes read
                     NULL);
            if(0==ret){

            }
#ifdef DEDUG_RX_CHAR
            LOG_DEBUG(COM,"%c", tempChar);
#endif
            if(0 < numberBytesRead) {
                if(BytesReadCnt < capasityRxArray) {
                    outRxArray[BytesReadCnt] = tempChar; // Store Tempchar into buffer
                }
            } else {
                loopRun = false;
            }
            BytesReadCnt++;
        };
        if(0 < BytesReadCnt) {
            *remain_len = BytesReadCnt;
            res = true;
        }
    }
#ifdef HAS_COM_PORT_DEBUG
    LOG_DEBUG(COM,"%s(): end", __FUNCTION__);
#endif
    return res;
}


bool init_serial(char* com_name, uint32_t baud_rate) {
    bool res = false;

    LOG_WARNING(COM,"try open [%s]... Rate: %u bit/s", com_name, baud_rate);

    hComm = CreateFile(com_name,
                       GENERIC_READ | GENERIC_WRITE, // Read/Write
                       0,                            // No Sharing
                       NULL,                         // No Security
                       OPEN_EXISTING,                // Open existing port only
                       0,                            // Non Overlapped I/O
                       NULL);                        // Null for Comm Devices
    LOG_INFO(COM,"hComm [%p]", hComm);
    if(INVALID_HANDLE_VALUE == hComm) {
        LOG_ERROR(COM, "Unable to open serial port [%s] ", com_name);
        res = false;
    } else {
        LOG_INFO(COM,"[i] Open [%s] OK ", com_name);

        com_set_params(hComm, baud_rate);
        com_set_timeout(hComm);
        res = true;
    }

    return res;
}

bool scan_serial(void) {
    bool res = false;
    clear_tui();
    LOG_INFO(COM,"Start new scan");
    bool out_res = false;
    char com_name[20] = "";
    uint8_t comPortNum;
    for(comPortNum = 0; comPortNum <= MAX_COM_NUM; comPortNum++) {
        snprintf(com_name, sizeof(com_name), "COM%u", comPortNum);
#if DEBUG_FAILED_OPENS
        LOG_DEBUG(COM,"  try [%s]...", com_name);
#endif
        HANDLE hComm;
#if 1
        hComm = CreateFile(com_name,
                           GENERIC_READ | GENERIC_WRITE, // Read/Write
                           0,                            // No Sharing
                           NULL,                         // No Security
                           OPEN_EXISTING,                // Open existing port only
                           0,                            // Non Overlapped I/O
                           NULL);                        // Null for Comm Devices
#if DEBUG_FAILED_OPENS
        LOG_DEBUG(COM,"  hComm [%p]", hComm);
#endif
        if(hComm == INVALID_HANDLE_VALUE) {
#if DEBUG_FAILED_OPENS
            LOG_DEBUG(COM," Unable open serial port [%s]", com_name);
#endif
        } else {
#if DEBUG_SPOT_COM
            LOG_DEBUG(COM,"  [%s] exists in PC", com_name);
#endif
            deviceList[comPortNum].isExistPort = true;
            CloseHandle(hComm);
            out_res = true;
        }
#endif

        snprintf(com_name, sizeof(com_name), "\\\\.\\COM%u", comPortNum);
        hComm = CreateFile(com_name, GENERIC_READ | GENERIC_WRITE, 0, // No Sharing
                           NULL,                                      // No Security
                           OPEN_EXISTING,
                           0,     // Non Overlapped I/O
                           NULL); // Null for Comm Devices
#if DEBUG_FAILED_OPENS
        LOG_DEBUG(COM,"  hComm [%p]", hComm);
#endif
        if(hComm == INVALID_HANDLE_VALUE) {
#if DEBUG_FAILED_OPENS
            LOG_DEBUG(COM,"   Error in opening serial port [%s]", com_name);
#endif
        } else {
#if DEBUG_SPOT_COM
            LOG_DEBUG(COM,"  [%s] exists in PC", com_name);
#endif
            deviceList[comPortNum].isExistPort = true;
            com_set_params(hComm, 9600);
            com_set_timeout(hComm);


            CloseHandle(hComm);
        }
    }

    print_device_list();
    LOG_DEBUG(COM," Scan done ");

    return out_res;
}


bool serial_init(void) {
    LOG_WARNING(SERIAL," Init");
    bool res = false;
    res = init_serial("COM3", 115200);
    return res;
}

{куча кода}

Ужас какой! Причем с ходу я в нем не увидел где задается осмысленное имя. Просто-то работать с COM-портом я умею, и вроде бы даже код не такой монструозный получился. Вероятно, просто потому что какие-то особенности игнорирую.


Под "читаемым именем" я имел в виду, что у моего устройства в дескрипторе прописано iFunction = "DBG" для одного CDC и iFunction = "STFLASH" для другого, соответственно система их читает и создает ссылки /dev/tty_DBG_0, /dev/tty_STFLASH_0, к которым обращаются что stm32flash (стандартная утилита прошивки), что stty (еще более стандартная утилита изменения настроек COM-порта). В результате мне не пришлось писать ни строчки кода под ПК.


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


А теперь мне придется ваш код читать, да еще и по ссылке сходить… Ну зачем я про это спросил...

как там из консоли менять параметры порта (скорость, четность)

О, нашел! Извиняюсь, что засоряю чужую тему, но иначе может потеряться: MODE.COM COM6 BAUD=50 > NUL

хорошая модель, лучше чем блю-пил..

жаль ацп только одно

Выводы порадовали. ))) Прототип - это и так временный вариант. Временный вариант временного варианта - это 5 ! Прототип, как правило, и выглядит всегда кустарно. Я так понимаю, что это Ваша первая плата STM и первый прототип, ну или второй. )))

А здесь оригинальный STM32 или клон/подделка как на дешевых BP v1 с алишки?
Купить оригинальный STM32 довольно проблематично стало

Поясните, пожалуйста, откуда взялось обозначение V2.0?

И чем плата с этим обозначением отличается от других вариантов?
https://github.com/WeActStudio/WeActStudio.MiniSTM32F4x1 - тут, например, говорится о версии V3.0

Если что-то сложнее общения с ПК по UART прототипировать собираетесь таким способом как у вас, то и с Nucleo ваше устройство превратится в клубок перемычек. BlackPill более компактная, легко встраивается в макет, и ее не так жалко, если что-то пойдет не так:

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории