Неважно, от кого исходит мысль. Главное, чтобы она была правильной и кого-нибудь унизила.
доктор Хаус

Меню навигации для мобильных

STM32 :: пытаемся разобраться

Автор zenon, 22 Окт., 2020, 00:20

« предыдущая - следующая »

zenon

Начал. :)
Вот только не пойму BOOT1 тут есть?
https://github.com/minamonra/

Slabovik

#1
Да, надо бы тему по STM32 поиметь. Контроллеры популярные, а для тех, кто хочет на них перейти, необходимы ответы на самые дилетантские вопросы.

Например, о выборе способа внутрисхемного программирования. Достаточно ли сигналов SWDIO и SWCLK, которые имеются на дешёвом ST-Link v2.

STM32_pgm.png

Мнения (https://www.drive2.ru/b/2228288/) разные (http://dimoon.ru/obuchalka/stm32f1/programmirovanie-stm32-chast-14-proshivaem-stm32-cherez-st-link.html), вот, например, одно из таких: Как прошить чип STM32 (https://blog.kvv213.com/2020/01/kak-proshit-chip-stm32/)
↓ спойлер ↓
Для подключения ST-LINK на STM32F103RB задействованы следующие выводы:

    SWDIO (Serial Wire Data Input/Output) – PA13
    SWCLK (Serial Wire Clock) – PA14
    TRACESWO (Trace of Serial Wire Output, та самая SWO) – PB3

Для работы через SWIM/SWD нужно-то всего два проводка, не забываем про обязательное соединение земли по всем подключенным устройствам. TRACESWO можно не подключать, особенно если данной функцией вы не пользуетесь, да и для отладки она, а не для загрузки прошивки.
[свернуть]
BOOT1 на PB2, похоже, выведен, а BOOT0 - он отдельной ногой. Хотя вот, насколько я понимаю, он всего лишь область памяти подставляет под "старт", как это делали, например, Радио-86, Орион-128 и многие другие. Чтобы там стал быть загрузчик, его надо туда записать? Или что-то там есть?
↓ спойлер ↓
При загрузке микроконтроллера, необходимо на вывод Boot0 микроконтроллера подать высокий уровень (3.3 В), а на вывод Boot1 низкий (0 В, соединить с землей). При выполнении этих двух условий микроконтроллер запустит прошитый на заводе в чип загрузчик.
[свернуть]
Аппнот (https://www.st.com/content/ccc/resource/technical/document/application_note/b9/9b/16/3a/12/1e/40/0c/CD00167594.pdf/files/CD00167594.pdf/jcr:content/translations/en.CD00167594.pdf) говорит, что там что-то есть
Цитата: undefinedThe bootloader is stored in the internal boot ROM (system memory) of STM32 devices, and is programmed by ST during production.
А есть ли какое-то стандартизированное расположение пинов у разъёма программирования SWD/SWC

Также добавлю ссылку на простейший ликбез (https://mcucpu.ru/index.php/ucontrollers/mcu/113-mikrokontrollery-cortex-m0m3m4) о то, чем отличаются процессоры с индесами A, R, M, а также версии ядер 0, 3, 4 и 4F

Получается, наш вариант - это процики ветки M (микроконтроллеры), а версия ядра (скорее, набора исполняемых команд) 0.

зы. Не вытерпел, обзавёлся такой платой, самоё дешёвой, что была в магазине
STM32_NUCLEO-F091RC.jpg
На борту (https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-nucleo-boards/nucleo-f091rc.html) STM32F091RCT6U (RAM:32k, Prog:256k - более чем достаточно для простых применений) + ST-Link, который можно юзать отдельно. Вот описательный даташит (https://www.st.com/resource/en/user_manual/dm00105823-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf) на эту серию плат - там расписаны коннекторы (на плате надписей недостаточно).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Даже не ожидал, что так быстро приедут.
Плату ещё не делал.
Корпус LQFP - практически тоже самое что и TQFP приложил к макетке для меги:
https://github.com/minamonra/

Slabovik

Похоже, это единственный вариант кузовов (32-ногие), у которых шаг вывода 0.8
Мне по-быстрому доступен только такой вариант (https://www.chipdip.ru/catalog/ic-microcontrollers?x.3725=sUaB&locid=tyumen-melnikayte-97&gq=stm32).
Вопрос ещё в том, что пока я готовлю тот макет, никаких мыслей не пришло, где бы мог быть нужен именно STM...
Лично мне хочется зацепить большой экранчик (точек на 400), например e-ink, но для вывода на такое надо всё-таки ОЗУ иметь значительных размеров. Ну или внешнее подцеплять, а для внешнего надо ноги, ноги... ATMega ведь тоже прекрасно умеет с внешним ОЗУ работать (64 или 100-ногие корпуса)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Подключил SWDIO, SWCLK, питание.
Проект тестовой платы в кикаде.
> .\ST-LINK_CLI.exe -C
STM32 ST-LINK CLI v3.0.0.0
STM32 ST-LINK Command Line Interface

ST-LINK SN : 57FF6E064984485758262287
ST-LINK Firmware version : V2J23S4
Connected via SWD.
SWD Frequency = 4000K.
Target voltage = 3.3 V.
Connection mode : Normal.
Device ID:0x444
Device flash Size : 32 Kbytes
Device family :STM32F030x4/F030x6
https://github.com/minamonra/

zenon

Сегодня попробовал поморгать через Куб, - блин почти 5 килобайт для дёрганья ногой... но отладка.
Решил что надо копнуть. Вот в эту сторону (https://github.com/eddyem/stm32samples/tree/master/F1-nolib/led_blink).
Для линукса там всё нативно, минимум изменять надо, только:
LDSCRIPT = stm32f030f.ld
MCU = F030x6
Но у меня всё как не у людей, мне сейчас windows нужна, блин, ну и пришлось make flash сделать вот так:
flash: $(BIN)
 @echo "  FLASH  $(BIN)"
 C:\STU\ST-LINK_CLI.exe -P $(BIN) 0x8000000 -Rst
Ну и тулчейн arm конечно нужен.
А так - собрал тот файл (https://github.com/eddyem/stm32samples/blob/master/F1-nolib/led_blink/main.c) - морзянкой мигает на PB3, 1130 байт, lss могу кинуть, нужен?
make flash у меня прикручен в Geany к F5.
Сборка F9...
Остальное пока - тёмный лес :)  :o
https://github.com/minamonra/

zenon

Всё туго пока... :)
Сижу пытаюсь понять.
Дело ещё в том, что на F030 примеров не так-то много.
Использую CMSIS.
Ну конечно ногодрыг первым делом.
В общем вот так вышло, используя задержку, которая "dummy"... назвал её delay.
#include "stm32f0.h"
void delay(uint32_t k) {
    volatile uint32_t i;
    for (i=0; i != 0x200*k; i++);
}

int main() {
    sysreset();

    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    //    #define  RCC_AHBENR_GPIOBEN                  ((uint32_t)0x00040000)        // GPIOB clock enable

    GPIOB->MODER |= GPIO_MODER_MODER3_0;
    //      #define GPIO_MODER_MODER3          ((uint32_t)0x000000C0)
    //      #define GPIO_MODER_MODER3_0        ((uint32_t)0x00000040)
    //      #define GPIO_MODER_MODER3_1        ((uint32_t)0x00000080)
    // __IO uint32_t MODER;        /*!< GPIO port mode register,              Address offset: 0x00 */
    GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR3;
    //        #define GPIO_OSPEEDR_OSPEEDR3    ((uint32_t)0x000000C0)
    //        #define GPIO_OSPEEDR_OSPEEDR3_0  ((uint32_t)0x00000040)
    // #define GPIO_OSPEEDR_OSPEEDR3_1  ((uint32_t)0x00000080)
    // __IO uint32_t OSPEEDR;      /*!< GPIO port output speed register,      Address offset: 0x08 */

  while(1) {

        GPIOB->BSRR = GPIO_BSRR_BR_3;
        delay(1000);
        GPIOB->BSRR = GPIO_BSRR_BS_3;
        delay(1000);

  }
}

// SysTick Interrupt
void SysTick_Handler(void) {
}
В комментах немного расшифровал дефайны из stm32f030x6.h
На PB3 частота 3,76 Hz.
Надо SysTick_Handler задействовать и посмотреть как можно с ним общаться.
https://github.com/minamonra/

zenon

#7
Блин, все грабли собрал, spi хардварный завёл только в кубе, и то - видеть его вижу на осциллографе - понять что он выводит - не могу... бред какой-то.
Но я его добью.
В итоге плюнул - написал всё ногодрыгом. :)
// дефайним ноги
#define latch_low()  GPIOA->BSRR |= GPIO_BSRR_BR_6; // reset
#define latch_high() GPIOA->BSRR |= GPIO_BSRR_BS_6; // set
#define data_low()  GPIOA->BSRR |= GPIO_BSRR_BR_7; // reset
#define data_high()  GPIOA->BSRR |= GPIO_BSRR_BS_7; // set
#define clock_low()  GPIOA->BSRR |= GPIO_BSRR_BR_5; // reset
#define clock_high() GPIOA->BSRR |= GPIO_BSRR_BS_5; // set

// функция отправки байта
void sspi_send_byte (uint8_t data) {
    for(int i=0; i<8; i++) {
        if  (((data>>i)&0x01)==1) data_high() else  data_low(); 
        clock_low();
        clock_high();
  }
}

...
    while(1) {
      latch_low();
      sspi_send_byte (0b11111110); // нижний правый
      sspi_send_byte (0b11111110); // восемь же!
      latch_high();
    }
...
Отправка восьмёрки выглядит так:
ы. Инит забыл:
    RCC->AHBENR |= RCC_AHBENR_GPIOAEN;  // GPIOA clock enable

    GPIOA->MODER    |= GPIO_MODER_MODER6_0;  // 6 = вход, 6_0 = выход, 6_1 = 
    GPIOA->OSPEEDR  |= GPIO_OSPEEDR_OSPEEDR6_0; // 6 low / 6_0 medium / 6_1 high
    GPIOA->BSRR    |= GPIO_BSRR_BR_6; // Установка низкого уровня

    GPIOA->MODER    |= GPIO_MODER_MODER5_0;  // 6 = вход, 6_0 = выход, 6_1 = 
    GPIOA->OSPEEDR  |= GPIO_OSPEEDR_OSPEEDR5_0; // 6 low / 6_0 medium / 6_1 high
    GPIOA->BSRR    |= GPIO_BSRR_BR_5; // Установка низкого уровня

    GPIOA->MODER    |= GPIO_MODER_MODER7_0;  // 6 = вход, 6_0 = выход, 6_1 = 
    GPIOA->OSPEEDR  |= GPIO_OSPEEDR_OSPEEDR7_0; // 6 low / 6_0 medium / 6_1 high
    GPIOA->BSRR    |= GPIO_BSRR_BR_7; // Установка низкого уровня
https://github.com/minamonra/

zenon

Про выбор IDE.
Поставил System Workbench for STM32 (https://www.openstm32.org/System%2BWorkbench%2Bfor%2BSTM32).
Основана на базе Eclipse.
Посмотрю приживётся или нет.
https://github.com/minamonra/

Slabovik

Целые выходные читал ИзиЭлектроникс - там целая подборка про STM32
http://we.easyelectronics.ru/blog/STM32/page1/
Жаль, что пока ни к чему не пришёл...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#10
В общем два варианта запуска TM1637 у меня получились, хард и софт.
Хардверный отсюда (https://github.com/rogerdahl/stm32-tm1637).
Софтверный вот тут (https://github.com/rogerdahl/stm32-tm1637).
Естественно переделал чуть.
Никакого open-drain как оказалось не надо, просто настроить пины как выход и скорость их работы.
Чтение клавиатуры фэйковое, вот тут (https://github.com/zhaolianyang/TM1637-by-stm32/blob/master/STM32%20_%20TM1637/HARDWARE/TM1637/TM1637.c) есть ещё один вариант, но его перевести в читабельный вид не осилил.
И ещё на HAL вариант (https://github.com/stDstm/Example_STM32F103/tree/master/TM1637_F103/Src).
Вот софт вариант:
#include "stm32f0.h"

// PIN PB6 = Clk // PIN PB7 = Dio
#define _tm1637ClkLow()  GPIOB->BSRR |= GPIO_BSRR_BR_6; // reset
#define _tm1637ClkHigh()  GPIOB->BSRR |= GPIO_BSRR_BS_6; // set
#define _tm1637DioLow()  GPIOB->BSRR |= GPIO_BSRR_BR_7; // reset
#define _tm1637DioHigh()  GPIOB->BSRR |= GPIO_BSRR_BS_7; // set

volatile uint32_t Tms = 0;
uint32_t cntr;

const char segmentMap[] = {
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, // 0-7
    0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, // 8-9, A-F
    0x00
};

#define FDEL 10

void _tm1637DelayUsec(unsigned int i) {
    for (; i>0; i--) {
        for (int j = 0; j < FDEL; ++j) {
            __asm__ __volatile__("nop\n\t":::"memory");
        }
    }
}

void _tm1637Start(void) {
    _tm1637ClkHigh();
    _tm1637DioHigh();
    _tm1637DelayUsec(2);
    _tm1637DioLow();
}

void _tm1637Stop(void) {
    _tm1637ClkLow();
    _tm1637DelayUsec(2);
    _tm1637DioLow();
    _tm1637DelayUsec(2);
    _tm1637ClkHigh();
    _tm1637DelayUsec(2);
    _tm1637DioHigh();
}

void _tm1637ReadResult(void) {
    _tm1637ClkLow();
    _tm1637DelayUsec(5);
    // while (dio); // We're cheating here and not actually reading back the response.
    _tm1637ClkHigh();
    _tm1637DelayUsec(2);
    _tm1637ClkLow();
}

void _tm1637WriteByte(unsigned char b) {
    for (int i = 0; i < 8; ++i) {
        _tm1637ClkLow();
        if (b & 0x01) {
            _tm1637DioHigh();
        }
        else {
            _tm1637DioLow();
        }
        _tm1637DelayUsec(3);
        b >>= 1;
        _tm1637ClkHigh();
        _tm1637DelayUsec(3);
    }
}

// Valid brightness values: 0 - 8.
// 0 = display off.
void tm1637SetBrightness(char brightness) {
    // Brightness command:
    // 1000 0XXX = display off
    // 1000 1BBB = display on, brightness 0-7
    // X = don't care
    // B = brightness
    _tm1637Start();
    _tm1637WriteByte(0x87 + brightness);
    _tm1637ReadResult();
    _tm1637Stop();
}

void tm1637Init(void) {
    RCC->AHBENR    |= RCC_AHBENR_GPIOBEN;  // GPIOB clock enable
    // PB6 Clk
    GPIOB->MODER  |= GPIO_MODER_MODER6_0;
    GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR6_1;
    // PB7 Dio
    GPIOB->MODER  |= GPIO_MODER_MODER7_0;
    GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR7_1;
    tm1637SetBrightness(8);
}

void tm1637DisplayDecimal(int v, int displaySeparator) {
    unsigned char digitArr[4];
    for (int i = 0; i < 4; ++i) {
        digitArr[i] = segmentMap[v % 10];
        if (i == 2 && displaySeparator) {
            digitArr[i] |= 1 << 7;
        }
        v /= 10;
    }
    _tm1637Start();
    _tm1637WriteByte(0x40);
    _tm1637ReadResult();
    _tm1637Stop();
    _tm1637Start();
    _tm1637WriteByte(0xc0);
    _tm1637ReadResult();
    for (int i = 0; i < 4; ++i) {
        _tm1637WriteByte(digitArr[3 - i]);
        _tm1637ReadResult();
    }
    _tm1637Stop();
}

void sys_tick_handler(void) { // Called when systick fires
    ++Tms;
}

void my_start_hse48_pll_3x16();

int main() {
    uint32_t TLED = 0;
    uint32_t counter = 0;
    sysreset();

    // На PA12 сидит мигалка
    RCC->AHBENR    |= RCC_AHBENR_GPIOAEN;  // GPIOA clock enable
    GPIOA->MODER  |= GPIO_MODER_MODER12_0;
    GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR12_0;

    SysTick_Config(6000, 1);

    tm1637Init();
    tm1637SetBrightness(3);

    while(1) {
        if(Tms - TLED > 99){
            if (counter<9999) counter++; else counter=0;
            TLED = Tms;
            pin_toggle(GPIOA, 1<<12);
            tm1637DisplayDecimal(counter, 0);
            if (counter==50)  tm1637SetBrightness(8);
            if (counter==100) tm1637SetBrightness(7);
            if (counter==150) tm1637SetBrightness(5);
            if (counter==200) tm1637SetBrightness(4);
            if (counter==250) tm1637SetBrightness(3);
            if (counter==300) tm1637SetBrightness(2);
            if (counter==350) tm1637SetBrightness(1);
            
        }
    } //while(1)
} // main()

При FDEL = 1 частота получилась 1.5 МГц - и оно таки работает на этой скорости. :)
Для 100 кГц FDEL = 10 у меня, тут всё зависит от тактовой частоты.
Видео (https://youtu.be/fJDJA4gs4cQ).
https://github.com/minamonra/

Slabovik

#11
http://itt-ltd.com/reference/ref_i2c.html
Цитата: undefinedпередача 8 бит данных от передатчика к приемнику завершаются дополнительным циклом (формированием 9-го тактового импульса линии SCL), при котором приемник выставляет низкий уровень сигнала на линии SDA, как признак успешного приема байта.
и даташит на TM о том же, страница 3
Цитата: undefinedTM1637 data transfer carries with answering signal ACK. For a right data transfer, an answering signal ACK is generated inside the chip to lower the DIO pin at the failing edge of the 8th clock. DIO interface wire is released at the end of the 9th clock.
Таким образом, оставляя высокий уровень на двутактном выходе в этот момент (а как его можно не оставлять?) можно получить в конце каждого байта короткое замыкание на линии SDA (DIO). Потому и используется открытый коллектор.

Выходов может быть несколько. Например, схемотехническое разделение направлений передачи сигнала на две ноги контроллера, примерно как здесь (https://5.imimg.com/data5/MA/OB/MY-1833510/tm1637-based-4-bits-red-digital-tube-led-display-module.pdf), либо перевод на время ожидания ACK вывода порта в состояние "вход с подтяжкой" (метод скользкий т.к. можно не успеть с переводом порта, да и лишние команды в цикле).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Дело в том, что если сконфигурировать порт как открытый сток нельзя подтянуть его к питанию (насколько я понял).
И если делаю так:
GPIOB->MODER  |= GPIO_MODER_MODER7_0; // пин как выход
GPIOB->OTYPER  |= GPIO_OTYPER_OT_7; // выход с открытым стоком
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR7_1; // скорость
То получаю вот такую картинку, и естественно передача не работает.
https://github.com/minamonra/

Slabovik

Это говорит либо о наличии чрезмерной ёмкости на шине (SCL, SDA), либо о чрезмерности сопротивления резистора подтяжки.
Ёмкости, которые есть на схеме, надо бы удалить. 100 пФ оптимальны для 400 кГц, не более (по спецификации ёмкости на шине не нужны, их установка преследует другие цели).
Можно попробовать ткнуть измерителем ёмкости на линии шины (относительно Gnd) и посмотреть, сколько там. Если больше 20-25 пФ - это плохо.
Можно (нужно бы) сделать опыт.
 1. отцепить приёмник.
 2. Установить православные резисторы подтяжки с вывода шины на Vcc питания STM, сопротивлением 2,2~4,7 кОм
 3. Установить в качестве паразитных конденсаторы 100 пФ
 4. Посмотреть осциллографом, что там будет происходить при попытке вывода туда чего-либо. Должна быть достаточно красивая картина со сглаженным передним фронтом вплоть до мегагерца.
 5. Убрать конденсаторы, подключить TM и наблюдать, как изменится картинка. По результатам наблюдений делать выводы.

зы: https://qastack.ru/electronics/102611/what-happens-if-i-omit-the-pullup-resistors-on-i2c-lines
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Да, согласен, опыт будет чуть позже.
По ссылке - какой ужасный перевод, аж читать сложно..  :o
И мысль пришла - мы же можем CLK сделать общую для двух линий, и повесить два модуля на три порта.
https://github.com/minamonra/

Slabovik

Можно. А шину данных нужно разделить.
Но если делать нормальную шину, нужно выбирать чипы с адресацией (если конечно нет цели работать с периферией параллельно).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Подтянул к VCC 3 кОм, конденсаторы 68 пик только нашлись, следующий номинал 220 пик, поэтому взял меньший.
Картинка 12 = 100 кГц без модуля, 13 тоже 100 кГц, но с подключенным модулем, работает нормально.
15-ая картинка - частота больше 500 кГц, модуль работает, от подключения/отключения модуля картинка не изменяется, фронты конечно похуже.
В общем картинка стала больше похожей на хардверный i2c, у него фронты похуже чем на этих картинках, но работает же, может так и должно быть.
При настройке портов в push-pull фронт конечно значительно лучше, прямоугольник отличный.
ы. Забыл сказать - при измерениях на модуле конденсаторы на линиях убраны, резисторы (10 кОм) стоят, забыл про них.
https://github.com/minamonra/

Slabovik

#17
Осталось выкинуть конденсаторы с шин SCL SDA и начать юзать (с резистором 3.3 кОм у STM) на типовой частоте 400 кГц.
А прямоугольник для I2C не упёрся т.к. фиксация SDA присходит по спаду импульса.

p.s. Добавил картинку
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

https://github.com/minamonra/

Slabovik

Потому что собственная входная ёмкость TM заявляется 15 пФ, что для цифровой микросхемы вообще-то очень много. Плюс ёмкости монтажа и пр. А на "типовой схеме" ещё плюс сотня и 10 кОм, которые и делают ситуацию катастрофической.
Именно поэтому I2C на больших скоростях не гоняют.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#20
Переделанная функция инициализации для открытого стока:
void tm1637Init(void) {
    RCC->AHBENR    |= RCC_AHBENR_GPIOBEN;      // GPIOB clock enable
    // PB6 Clk
    GPIOB->MODER  |= GPIO_MODER_MODER6_0;      // пин как выход
    GPIOB->OTYPER  |= GPIO_OTYPER_OT_6;        // выход с открытым стоком
    GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR6_1;
    // PB7 Dio
    GPIOB->MODER  |= GPIO_MODER_MODER7_0;
    GPIOB->OTYPER  |= GPIO_OTYPER_OT_7;        // выход с открытым стоком
    GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR7_1;
    tm1637SetBrightness(4);
}
Едем дальше, сегодня мучал кнопку.
Повесить решил её на прерывние по спаду, называется оно EXTI.
Кнопка на PA0, подтянута к VCC через 10 кОм.
Опять наткнулся на то, что для F1 куча примеров, для F0 - нет.
Нашёл пример тут (https://radiokot.ru/forum/viewtopic.php?f=59&t=126219).
Но грабли у меня продолжились - стандартное название обработчика EXTI0_1_IRQHandler() - у меня не заработало, тут надо учитывать, что у меня не стандартная CMSIS, и рабочая среда - Geany.
В общем - решил, название обработчика у меня оказалось exti0_1_isr().
Вот такой получился рабочий вариант:
void EXTI_Init(void) {
    RCC->APB2ENR |=RCC_APB2ENR_SYSCFGCOMPEN;        // тактирование SYSCFG
    SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI1_PA;    // PA0 // Bit definition for SYSCFG_EXTICR1 register @file stm32f030x6.h
    RCC->APB2ENR &= ~RCC_APB2ENR_SYSCFGCOMPEN;
    EXTI->IMR = EXTI_IMR_MR1;                      // демаскируем прервание по 1 линии
    EXTI->FTSR = EXTI_FTSR_TR1;                    // прерывание по спаду  //разрешаем срабатывание по переднему фронту
    EXTI->RTSR = EXTI_RTSR_TR1;                    // прерывание по фронту    //разрешаем срабатывание по заднему фронту 
    NVIC_EnableIRQ(EXTI0_1_IRQn);                  // разрешаем прерывание в контроллере прерываний
    NVIC_SetPriority(EXTI0_1_IRQn,0);
}

// обработка внешнего прерывания у stm32f0 прерывания 0 и 1 вместе
void  exti0_1_isr(){
    if (EXTI->PR & EXTI_PR_PR1) {  // если бит прерывания установлен
        EXTI->PR |= EXTI_PR_PR1;    // сбрасываем флаг прерывания
        counter=5000;
    }
}
Тут counter у меня считает на дисплее, при нажатии кнопки просто устанавливает другое значение.
+++
Тут есть желание разобраться с IR пультом, насколько я понимаю тут тоже можно работать через это же прерывание?
Нужен PWM input mode.
https://github.com/minamonra/

zenon

Добрался до libopencm3.
Старт и мигалка из примеров, необходимые действия для stm32f030k6t6.
Подготавливаем каталог для библиотек, пусть будет ~/stm32 и клонируем библиотеки и примеры.
# cd ~
# mkdir stm32 && cd stm32
# git clone https://github.com/libopencm3/libopencm3.git
# git clone https://github.com/libopencm3/libopencm3-examples.git
Линкуем каталог с библиотеками в каталог для библиотек с примерами, сначала удалив пустой libopencm3/ в примерах (можно скопировать, но нужно ли плодить?).
# rm -r ~/stm32/libopencm3-examples/libopencm3/
# ln -s ~/stm32/libopencm3 ~/stm32/libopencm3-examples/libopencm3
Собираем библиотеку:
# cd ~/stm32/libopencm3
# make
Создаем каталог для нашего микроконтроллера и копируем каталог miniblink от stm32f0-discovery и линкер, который потом переименовываем.
# cd ~/stm32/libopencm3-examples/examples/stm32/f0/ 
# mkdir stm32f030k6t6
# cp -r stm32f0-discovery/miniblink stm32f030k6t6
# cp stm32f0-discovery/stm32f0-discovery.ld stm32f030k6t6.ld
Открываем линкер:
# vim stm32f030k6t6.ld
Изменяем rom и ram на наши (32К и 4K), те так:
MEMORY
{
 rom (rx) : ORIGIN = 0x08000000, LENGTH = 32K
 ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
}
Все.
Заходим в каталог miniblink, правим в Makefile строчку LDSCRIPT = ../stm32f030k6t6.ld, смотрим код, компилируем, прошиваем и радуемся... :)))
# cd miniblink 
# vim miniblink.c


# make miniblink.stlink-flash
  FLASH  miniblink.bin
/usr/bin/st-flash write miniblink.bin 0x8000000
st-flash 1.5.1
2020-11-22T18:07:27 INFO common.c: Loading device parameters....
2020-11-22T18:07:27 INFO common.c: Device connected is: F0 small device, id 0x10006444
2020-11-22T18:07:27 INFO common.c: SRAM size: 0x1000 bytes (4 KiB), Flash: 0x8000 bytes (32 KiB) in pages of 1024 bytes
2020-11-22T18:07:27 INFO common.c: Attempting to write 500 (0x1f4) bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x08000000 erased
2020-11-22T18:07:27 INFO common.c: Finished erasing 1 pages of 1024 (0x400) bytes
2020-11-22T18:07:27 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
2020-11-22T18:07:27 INFO flash_loader.c: Successfully loaded flash loader in sram
  1/1 pages written
2020-11-22T18:07:27 INFO common.c: Starting verification of write complete
2020-11-22T18:07:27 INFO common.c: Flash written and verified! jolly good!

make V=1 - verbose output, можно опустить.
make flash хочет сразу работать через openocd, пока вываливается с ошибкой у меня...
make bin делает бинарник, make по умолчанию делает elf.
https://github.com/minamonra/

zenon

Прикрепляю рабочий вариант софт i2c для libopencm3, основное тут Makefile, в нём указывается путь для библиотеки и необходимые файлы...
Выдергивал из официальных примеров. Документация тут (https://github.com/libopencm3/libopencm3/wiki) и тут (http://libopencm3.org/docs/latest/html/).
Всё в одном каталоге, включая ld.
По умолчанию хочет Black magic probe, надо бы его сделать и попробовать отладку.
Как прошить в комментариях.
https://github.com/minamonra/

Slabovik

Не пробовал https://www.segger.com/products/development-tools/embedded-studio/ ?
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Нет, не пробовал. st-link не поддерживается... Надо в j-link перешиться.
Скачал, не пойму пока как стартанут чего-нибудь.
https://github.com/minamonra/

Slabovik

#25
У них вроде конвертатор есть (фирмварь ST-link'а заменяют с возможностью восстановления обратно).
https://www.segger.com/products/debug-probes/j-link/models/other-j-links/st-link-on-board/
Надо пробовать, но я завис на лабораторнике, а тянуть несколько дел одновременно не получается.

upd: у китайцев J-Link имеется, вполне вменяемо. https://sysadmin.link/?p=2522
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Ну вот, только я засел на линукс... опять перегружаться... :)
offtop:
Я пока собрал вот так (пидб), на выходе 4хtip35с.
... получилась неплохая полочка на столе... сверху ещё кусок фанеры, брутально короче. :)))
https://github.com/minamonra/

zenon

#27
Не, пока не могу/хочу ничего из того, что видел, да понимаю, что тыкаюсь в geany неэффективно... но нужна ли мне эффективность? Если мне всего-то и надо помигать, почитать ацп...
Перешил в j-link - опять какие-то лицензии... да епрст.
Мне надо, чтобы было просто и понятно, вот редактор, вот терминал, командная строка, make etc.
Взглянув очередной раз на Hal, куб, эклипс понял что пока они у меня вызывают стойкое отвращение, может что-нибудь изменится...
Вот например интерфейс ардуины - аж вздрагиваю, к тому, что на яве наверное никогда не привыкну.
Но это сугубо моё отношение на данный момент. :))
https://github.com/minamonra/

zenon

Решил посмотреть как через чтение пина работать будет, и будет ли вообще... :)
Источник (https://controllerstech.com/ir-remote-with-stm32/).
Моё видео (https://youtu.be/SJL2UuyYyBY).
Оно то конечно не для использования, так - побаловаться.
https://github.com/minamonra/

zenon

#29
Решил бороться со своими привычками, они - зло.
Ну сильная же штука segger, видно сразу, тут хоть часть от неё поиспользовать.
... и мимо usart никак не пройти, разбирался вчера и с тем и с тем, попутно освежив в памяти по диагонали разницу между uart и usart, опять-же плохая привычка оставлять разборки деталей на потом.
В итоге в segger запустил usart? (или uart :) ) по-простому передачу и прерывание на приём, можно было сразу с dma выдрать - примеров предостаточно, но это не наш подход.
Попробовал и в линукс и в вин, всё работает, терминал, дебаг etc.
Не разобрался пока как переменные в отслеживание добавить...
Код в процессе... лишних комментариев из разных мест много, не всё правильно.
Передаёт в терминал символы, при приёме любого зажигает на чуть-чуть светодиод.
https://github.com/minamonra/

Slabovik

#30
Вот ещё описание интересного инструмента от Segger https://habr.com/ru/post/259205/
и ссылка на официальную страничку https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/
Насколько я понимаю, для использования нужно только включить библиотеку в код и пользоваться её функциями для вывода чего-то нужного для отладки.

зы: USART - более сложное устройство т.к. является синхронным.  Краткий болтологический ликбез: https://www.rlocman.ru/review/article.html?di=164400

Кстати, SPI - это типичный USART, I2C - тоже, как и множество других. Самый прикольный пример USART - манчестерский код, который можно было просто записывать на магнитофон, чем и пользовались для хранения программ во времена Радио-86 (гы: Spectrum этот код не использовал, из-за чего вывод-ввод на магнитофон у него был более унылым).

p.s. и просто оставлю чуток ссылок в копилку ликбеза: как прошить STM32

https://blog.kvv213.com/2020/01/kak-proshit-chip-stm32/
http://dimoon.ru/obuchalka/stm32f1/programmirovanie-stm32-chast-14-proshivaem-stm32-cherez-st-link.html
https://www.drive2.ru/b/2228288/
http://microsin.net/programming/arm/um1075-st-link-debugger-programmer-for-stm8-and-stm32.html
Ну а что, пусть будет  :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Пытаюсь понять как назначаются альтернативные функции.
Меня сейчас интересует таймер TIM1_CH1.
Даташит тут (https://www.st.com/resource/en/datasheet/stm32f030f4.pdf). Reference manual (https://www.st.com/resource/en/reference_manual/dm00091010-stm32f030x4-x6-x8-xc-and-stm32f070x6-xb-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf).
В сниппетсах это делается так:
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) | (GPIO_MODER_MODER8_1);
GPIOA->AFR[1] |= 0x02;
у меня получилось методом непонятным... вот так:
GPIOA->MODER  |= GPIO_MODER_MODER8_1; 
GPIOA->AFR[1] |= (0x2 << 0*4);
Никак я с такими выкрутасами не разберусь... что и откуда берётся и как складываются две тетрады.
Для PA8 TIM1_CH1 это AF2. Весь байт значит у нас находится в GPIOA->AFR[2] судя из даташита... почему в примере AFR[1]?
И как получилось что он равен 0x02?
https://github.com/minamonra/

zenon

Дабы разобраться с битовыми операциями запустил отладку и смотрю что и куда.
В коде специально подробно для себя расписал...
Приёмник работает. :)
https://github.com/minamonra/

zenon

Эммм, начал искать vref у stm32f030, а его отдельного и нету. Он на vdda.
Цитата: undefinedVref- and Vref+ are only available as external pins on LQFP144, UFBGA132, LQFP100, UFBGA100, and TFBGA64 packages, otherwise they are bonded to Vssa and Vdda, respectively.
И куда же тогда точную опору ставить? На vdda/vssa? Какое напряжение тогда?
https://github.com/minamonra/

Slabovik

Если я правильно понимаю, VDDA ровно такой же, как и Vcc, просто питает он аналоговые цепи. Точность и "плывучесть" со всеми вытекающими напрямую зависит от точности и плывучести применённого стабилизатора питания (его можно индивидуально для VDDA забацать).

Для проведения точный измерений в таком случае всё-равно внешний АЦП с прецизионной опорой ставить, ну, или искать чип, где Ref выведен наружу...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

VREF+ and VREF– inputs are available only on 100-pin packages.
Не понимаю я почему такая политика у них, вроде есть 12 бит... а тут так.
https://github.com/minamonra/

zenon

Хм. Кнопки, может стоило в отдельнцю тему закинуть.
Набрёл на вот такое решение (https://www.microchip.su/showthread.php?p=76885#post76885).
Лучше конечно заранее один порт например отводить под кнопки, тогда его удобно целиком читать.
Но у меня немного в разнобой вышло, но адаптировать под stm код получилось.
button.h
#define BUTTON0     1<<0  // PA1
#define BUTTON1     1<<1  // PA2
#define BUTTON2     1<<2  // PA3
#define BUTTON3     1<<3  // PA4
#define BUTTON4     1<<4  // PA11
#define BUTTENC     1<<5  // PA5
#define BUTTONB     1<<6  // PA5
//#define BUTTONB     (1<< 1)  // PB5
//#define BUTTON7     (1<< 1)  // PB5
#define pin_BUTTON0     pin_read(GPIOA, 1<<1)
#define pin_BUTTON1     pin_read(GPIOA, 1<<2)
#define pin_BUTTON2     pin_read(GPIOA, 1<<3)
#define pin_BUTTON3     pin_read(GPIOA, 1<<4)
#define pin_BUTTON4     pin_read(GPIOA, 1<<11)
#define pin_BUTTENC     pin_read(GPIOA, 1<<5)
#define pin_BUTTONB     pin_read(GPIOB, 1<<1)

typedef struct
{
    unsigned char   ucPressed;            // Состояния кнопок
    unsigned char   ucJitter    : 3;      // Для подавления дребезга
    unsigned char   ucCounter   : 6;      // Для проверки удерживания
    unsigned char   ucHold;               // Состояние длительного удерживания
                                          // События:
    unsigned char   ucShort;              // Короткое нажатие и отпускание
    unsigned char   ucJustPressed;        // Кнопки только что нажаты
    unsigned char   ucJustReleased;       // Кнопки только что отпущены
    unsigned char   ucChanged;            // Состояние кнопок изменилось

} TBUTTONS;

extern volatile TBUTTONS m_Buttons;

void Task_Buttons (void);
button.c
void Task_Buttons (void)
{
    static  unsigned char s_ucPort;     // Предыдущее состояние портов
            unsigned char ucTemp;       

    ucTemp = 0x00;

    if (!pin_BUTTON0) ucTemp |= 0x01;
    if (!pin_BUTTON1) ucTemp |= 0x02;
    if (!pin_BUTTON2) ucTemp |= 0x04;
    if (!pin_BUTTON3) ucTemp |= 0x08;
    if (!pin_BUTTON4) ucTemp |= 0x10;
    if (!pin_BUTTENC) ucTemp |= 0x20;

    if (!pin_BUTTONB) ucTemp |= 0x40;
    //if (!pin_BUTTON7) ucTemp |= 0x80;

    ucTemp      ^= s_ucPort;            // Изменения с момента последнего выполнения
    s_ucPort    ^= ucTemp;              // Текущее состояние портов

    if (ucTemp)                         // Подавление дребезга
    {
        m_Buttons.ucJitter = 0;
        return;
    }
    //------------------------------------------------------------------------------
    
    m_Buttons.ucJitter++;
    ucTemp = s_ucPort ^ m_Buttons.ucPressed;// Маска изменения состояние

    if (!m_Buttons.ucJitter && ucTemp)      // Состояние изменилось, дребезг подавлен
    {
        //------------------------------------------------------------------------------
        //  Проверяем отпущенные кнопки
        //------------------------------------------------------------------------------
        
        if (m_Buttons.ucPressed & ucTemp)   // Есть отпущенные
        {
            m_Buttons.ucJustReleased    |=  ucTemp;
            m_Buttons.ucChanged         |=  ucTemp;
            m_Buttons.ucShort           |=  ucTemp;
            m_Buttons.ucShort           &= ~m_Buttons.ucHold;
            m_Buttons.ucHold            &= ~ucTemp;
        }   // if released
        //------------------------------------------------------------------------------
        //  Проверяем нажатые кнопки
        //------------------------------------------------------------------------------
        if ((m_Buttons.ucPressed ^ ucTemp) & ucTemp)    // Есть нажатые
        {
            m_Buttons.ucChanged         |=  ucTemp;
            m_Buttons.ucJustPressed     |=  ucTemp;
            m_Buttons.ucShort           &= ~ucTemp;
        }   // if pressed

        m_Buttons.ucPressed = s_ucPort;
        m_Buttons.ucCounter = 0;
    }   // if changed and debounced

    m_Buttons.ucCounter++;
    //------------------------------------------------------------------------------
    //  Проверяем длительное удерживание
    //------------------------------------------------------------------------------
    
    if (m_Buttons.ucCounter == 50)
    {
        m_Buttons.ucChanged |= m_Buttons.ucHold ^ m_Buttons.ucPressed;
        m_Buttons.ucHold = m_Buttons.ucPressed;
    }

}

Использование вышло вот таким:
void button_process(uint8_t i) // начало кнопок
{ if (buttms > ttms || ttms - buttms > i) {
  buttms = ttms; 
  Task_Buttons();

  if (m_Buttons.ucShort & BUTTONB) { m_Buttons.ucShort = 0; }
  if (m_Buttons.ucHold  & BUTTONB) { }

  if (m_Buttons.ucShort & BUTTON0) { m_Buttons.ucShort = 0; }
  if (m_Buttons.ucHold  & BUTTON0) { }

  if (m_Buttons.ucShort & BUTTON1) { m_Buttons.ucShort = 0; }
  if (m_Buttons.ucHold  & BUTTON1) { }

  if (m_Buttons.ucShort & BUTTON2) { m_Buttons.ucShort = 0; }
  if (m_Buttons.ucHold  & BUTTON2) { }

  if (m_Buttons.ucShort & BUTTON3) { m_Buttons.ucShort = 0; }
  if (m_Buttons.ucHold  & BUTTON3) { }

  if (m_Buttons.ucShort & BUTTON4) { time_setup();m_Buttons.ucShort = 0; }
  if (m_Buttons.ucHold  & BUTTON4) { }
  }
Функция button_process вызывается в main, в аргументе передаётся задержка, сколько раз в миллисекундах выполнить опрос.
Всё бы ничего, но вот как тут организовать повторы с инкриментом переменной при длительном удержании не могу понять... :)
https://github.com/minamonra/

zenon

Блин, ucPressed отлично на повторы работает, разобрался.
https://github.com/minamonra/

Slabovik

Я вот несколько дней кручу-верчу этот код и... реально туплю (поди совсем старый стал).
Мог бы ты пояснить, как именно он работает. Интересуют конечно не конкретные команды, а принцип.
Вообще, тема опроса кнопок довольно обширна. ВО времена ДОСа и мелких компов я тоже писал работу опрос клавиатуры. Можно пообсуждать.

Мастер, выделишь тему отдельно?
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Ну, кроме объяснений, которые там (на микрочипе) сказать вряд-ли что смогу, я его просто адаптировал под stm.
Сам не до конца понимаю как это работает.
Да, дефайн (тоже где-то подсмотрен...) pin_read забыл.
Вот так:
#define pin_read(gpioport, gpios) (gpioport->IDR & (gpios) ? 1 : 0)
Порт можно прочитать целиком так:
Port=GPIOB->IDR;
https://github.com/minamonra/

zenon

Ещё на одно решение для кнопок попалось на глаза:
Вот тут (https://qna.habr.com/q/578105#answer_1327207).
Код скопировал с комментами.
↓ спойлер ↓
//Я, например, вот так сделал (для обработки коротких и длинных нажатий трёх кнопок):
uint8_t buttonFlags = 0; // буфер флагов зарегистрированных нажатий

// ОПРОС КНОПОК
void buttons_check(void)
{
  uint8_t buttonState = 0; // буфер для текущего состояния кнопок
  static uint8_t buttonPressCounter[] = { 0, 0, 0 }; // счётчики продолжительности нажатия кнопок
  buttonState = ( BUTTON0_STATE | BUTTON1_STATE << 1 | BUTTON2_STATE << 2 ) ^ 0x07;
  for ( uint8_t i = 0; i <= 2; i++ ) // для каждой из 3 кнопок проверяем:
  {
    if ( ((buttonState >> i) & 1) ) // если кнопка нажата
    {
      buttonPressCounter[i]++; // увеличиваем значение счётчика
      if ( buttonPressCounter[i] == 3 ) // если кнопка нажата около 50мс
      {
        buttonFlags |= ( 1 << i ); // фиксируем короткое нажатие
      }
      else if ( buttonPressCounter[i] == 95 ) // если кнопка нажата около 1.5 секунд
      {
        buttonFlags |= ( 1 << i ) | ( 1 << (i+3) ); // фиксируем длинное нажатие
        buttonPressCounter[i] = 80; // повторяем действие длинного нажатия при удерживании кнопки (примерно 4 раза в секунду)
      }
      else
      {
      buttonFlags &= ~( 1 << i ); // сбрасываем флаг короткого нажатия кнопки
      }
    }
    else // если кнопка отпущена
    {
      buttonPressCounter[i] = 0; // сбрасываем счётчик
      buttonFlags &= ~( ( 1 << i ) | ( 1 << (i+3)) ); // сбрасываем флаги нажатия кнопки
    }
  }
}
//Функция вызывается в теле обработчика прерывания по таймеру, перед функцией - обработчиком нажатий, 
//в зависимости от текущего состояния переменной buttonFlags, и там же, делаем другие нужные нам вещи,
//типа обновления информации на дисплее, и т.д.) Таким образом, всего одним таймером убиваем сразу всех зайцев :)
//P.S. Значения счётчиков - 3, 80 и 95 - с расчётом, что прерывание по таймеру срабатывает каждые 16мс.
//P.P.S. Если чё, сильно не пинять - я ни разу не программист, а так, балуюсь немножко ))
[свернуть]
Надо попробовать как оно работает.
https://github.com/minamonra/

zenon

#41
Ковыряю 1.77" TFT дисплей.
Переделал вот этот вариант (https://forum.easyelectronics.ru/viewtopic.php?f=35&t=13281&start=50), который изначально был для F4 на F0.
Проект тут (https://github.com/minamonra/my_stm_snippets/tree/main/F0/vitgo_st7735).
С переферией разобрался, пытаюсь понять как со шрифтами работать, пока не понял.
Вот интересная ссылка (http://www.rinkydinkelectronics.com/h_utft_fonts_101.php).

+++
↓ спойлер ↓
[свернуть]
https://github.com/minamonra/

zenon

Мы его душили - душили...
↓ спойлер ↓
[свернуть]
https://github.com/minamonra/