Hledám učitele STM32F a uVision

Zdravím,

dost umím programovat PICe, ale teď jsem poodkryl skvělé vlastnosti procesorů řady STM32F. Byl by zde někdo, kdo by mě chtěl na dálku (Skype atd) naučit základy programování pod uVision 5? Potřeboval bych vědět kam šáhnout pro informace o perifériích, jaká jsou k tomu makra, jak by měla vypadat kompletní initializace atd.
Samozřejmě to nechci zadarmo. Ale viděl bych to max 300 kč / hod.

Popřípadě neznáte nějaký dobrý seriál pro začátečníky?
Ale ne pro nějaký vývojové kity. Protože tam nešáháte přímo na procesor, ale pouze používáte makra, které někdo předvytvořil. Bohužel znalost těchto maker vám nijak nepomůže u jiného kitu nebo samotného procesoru.

Díky
Fano

A máš nějakej vlastní HW na kterým to budeš zkoušet ? Máš to čím programovat / debagovat ? Kterej konkrétní procesor tam máš ? Vyvíjím na UV4 s M0 a M3 od STM. Všechny potřebné faktické informace najdeš v “reference manual” a “programming manual” pro konkretní rodinu procesorů (stáhni si ze stránek STM).

Mám DPS která má asi nahradit Arduino mini a debuger ST-link V2 (viz foto).
Je tam STM32F103C8 což je tuším M3. Samozřejmě že jsem si už “reference manual” a “programming manual” už pročítal, ale prostě jsem tam nenašel kompletní, pochopitelný postup pro třeba inicializaci USARTu. Podle nějakých youtube tutoriálů jsem sice už rozblikal LEDku, ale to bylo spíše štěstí. Šlo to sice jak po másle, ale já bych radši chtěl vidět do nitra. U PICů jsem hlavně šahal na bity a bajty přímo procesoru. Tady u toho se mi zdá že je to samé makro a struktura. Nebráním se tomu, ale musím si je osvojit a to třeba z nějakých příkladů či seriálů (tutoriálů).
Třeba jako základ inicializace si myslím, že by mělo být: nastavit konfigurační slova (pokud nějaké má), nastavit frekvenci, nastavit všechny piny (analog/digital, vstup/výstup). Tyto všechny věci dělám u PICe jako první a řekl bych že na to mám pěkně vytvořený univerzální knihovny, které bych chtěl tvořit i na tento STM. Taky bych potřeboval vidět jak veliké by měli být ty knihovny a taky jakou zvolit strukturu a systém uspořádání.
Kód píšu v prostředí uVision 5. Oproti starším verzím mi přijde mnohem jednodušší v prvotním nastavení celého projektu. A dost jsem slyšel, že je nejlepší na pokročilé projekty, které rozhodně chci časem dělat.

Byl by jsi tedy ochotný mě pár hodin učit přes vzdálenou plochu?

Fano

Fain, se 103 dělám.

Tady taky nastavuješ jednotlivé bity v registrech.
Asi ne tak triviálně jako BSF nebo BCF u piců.
Ale co Ti vadí na tom, že v hlavičkovém souboru stm32f10x.h jsou registry pro jednotlivé periférie organizované do struktury ? Jinak by se totiž musel nadefinovat každý registr zvlášť. To by znamenalo akorát větší prostor pro chybu. Navíc, pokud má mcu 3 usarty, nadefinují se jen 3 ukazatele na strukturu usart a né 3x7 solo registrů. Pokud Ti toto nevyhovuje, můžeš si nadefinovat vlastní struktury. Dokonce se to dá udělat i tak, že přistupuješ k jednotlivým bitům podle jména, ale je to podle mě zbytečná práce :wink:

Takže pokud si blikal ledkou, víš že, když chceš použít nějakou periferii, musíš jí zapnout hodiny. Pak si ji nastavíš jak potřebuješ a pak už jen používáš.

Obvykle konfiguruju v tomhle pořadí:

Systémový kmitočet (XTAL, PLL, atd…)
Povolení hodin pro periferie které se chystám využívat
Nastavení SYSTICK
Nastavení PERIFERIE1
Nastavení PERIFERIE2
.
.
.
Nastavení IRQ (povolení, priorita, globání povolení)

Univerzální knihovna bude vždy větší (a pomalejší) než kód nezbytně nutný pro nastavení toho co potřebuješ. To už si tam můžeš rovnou zatáhnout SPL nebo CMSIS.

uVision4 je prakticky stejné jako uVision5 akorát to má jinak update zařízení a ještě do toho zatáhli různé SW balíčky. Mě je sympatičtější UV4.

Když sem hodíš konkrétní dotaz na konkrétní problém, rád odpovím (budu-li umět) Online fakt ne :slight_smile:

Nevadí mi že se často používají makra a struktury. Může to být dobrý a časem i svižný, ale jen říkám, že popis maker a vnitřku struktur není dost dobře popsán v .h souborech. Čekal bych nějaký vysvětlení a třeba i příklad použití v nějakém PDFku. Teď jsem přišel na to, že to má jisté zákonitosti a že jsem schopen celé makro či strukturu pochopit pomocí ostatních .h souborů nebo reference manualu. Není to sice jednoduchá cesta k pochopení, ale nakonec se doberu konce.
Otázkou je jestli existují nějaké stanovené postupy, jak třeba inicializovat periferie. Jaký registry nastavit nejdřív, jaký pak, jaký nenastavovat vůbec protože jsou defaultně správně nastaveny. Jaké parametry předávat inicializačním funkcím, a jaké nastavovat natvrdo, atd. Zkrátka pokud si už s tím někdo lámal hlavu a vytvořil nějaké postupy a makra tak bych je rád znal / používal. Protože nemá cenu si psát něco, co už jiní dávno vymysleli a sepsali. Asi bych se potřeboval poučit z nějakých příkladů / tutoriálů / hotových projektů, které jsou doporučovaný nejlépe od Keil nebo ST.
Tuším, že většina maker a struktur je součástí CMSIS a koukám, že o CMSIS je toho na keil.com hromada. Tak třeba tam naleznu inspiraci, jakým způsobem psát kód aby byl co nejvíce univerzální pro moje ostatní projekty. Protože to co jsem za ty léta programování PICů pochopil je, že mít dobře udělaný univerzální knihovny je ta největší výhoda.
Pak jsem se ještě dozvěděl, že existuje nástroj STM32Cube což jsou knihovny nejvyšší úrovně abstrakce. Ale do toho se pustím, až budu potřebovat využívat opravdu složité funkce typu TCP/IP nebo FAT.
Takže teď si asi pročtu všechno na keil.com a pak asi budu pokládat jednotlivé dotazy. Díky za jejich případné zodpovězení, ale to online doučování bych vážně chtěl a nebojím za to pustit nějaký peníz. Doufám, že i můj zaměstnavatel něco přispěje, pokud to pak využiji ve firmě. Proto se pak tady zeptám všech ještě jednou.
Zatím moc dík za informace :slight_smile:
Fano

Žádný hlubší popis struktur v souboru stm32f10x.h nepotřebuješ. Struktura se jmenuje jako periferie a členové struktury (registry) se jmenují stejně jako v pdf reference manual. Tam je i default každého registru a pokud je vyžadován nějaký specifický postup při nastavování periferie, je to tam zmíněno.

No a to ostatní - prostě programuj a uvidíš. Největší výhoda je umět si napsat vlastní kód :wink:

Ahoj, posílám ti příklad mé inicializace MCU resp. main.c jak to používám já…netvrdím, že je to bůhvíjak skvělý přístup, ale na druhou stranu píšu už dost složité projekty abych byl schopen tvrdit, že tento systém je odzkoušený a spolehlivý. Snažím se nepoužívat žádné knihovny třetích stran a vycházím jen z toho co je v datasheetu a v knihovne stm32f10x.h popr. core_cm3.h

#include <stdint.h>
#include <stm32f10x.h>
#include <stm32f10x_flash.h>

#include <watchdog.h>
#include <memory_map_bios.h>
#include <var_bios.h>
#include <led_driver.h>

#include "main.h"

static void init_ram_memory(void);
static void init_mcu(void);

extern void init_gpio(void);
extern void init_slave_komunikace(void);
extern void init_rtc(void);
extern void init_adc(void);
extern void init_beeper(void);
extern void init_led(void);
extern void init_wifi(void);
extern void handle_rtc(void);
extern void handle_gpio(void);
extern void handle_adc(void);
extern void handle_slave_komunikace(void);
extern void handle_led(void);
extern void handle_wifi(void);

int main(void){   
  init_mcu();                                                                   // init HW MCU
  FLASH_Unlock();                                                               // povoli zapis do FLASH pameti MCU - emulace EEPROM
  init_ram_memory();                                                            // vymazani pametovych oblasti  
  
  init_gpio();
  init_slave_komunikace();
  init_rtc();
  init_beeper();
  init_adc();
  init_led();
  init_wifi();
  
  SysTick_Config(SystemCoreClock/1000);                                          //10-100ms,100-10ms,200-5ms,1000-1ms,10000-100us,100000-10us,
  init_wdt(TIMEOUT_13600MS);
  
  for(;;){
    handle_wdt();                                                               //obsluha Watchdog timeru
    handle_rtc();                                                               //obsluha realneho casu
    handle_adc();                                                               //obsluha analogu pres DMA
    handle_gpio();                                                              //obsluha vstupne-vystupnich portu    
    handle_led();                                                               //obsluha led - mela by byt tesne za obsluhou gpio
    handle_wifi();
    handle_slave_komunikace();                                                  //obsluha mirkom slave komunikace    
    // zde je umistena regulace - na konci hlavni smycky ///////////////////////
    //handle_application();
    }
  }

static void init_ram_memory(void){
  uint32_t i;
  
  #if defined(RAM_ADC)  
  //sem se plni analogove hodnoty z ADC pomoci DMA
  for(i = 0; i < RAM_ADC_SIZE; i+=4){ *(uint32_t*)(RAM_ADC_ADDR + i) = 0;}   
  #endif

  #if defined(RAM_BIOS)
  //toto je oblast pameti v BIOS, ktera je adresovana absolutne
  for(i = 0; i < RAM_BIOS_SIZE; i+=4){ *(uint32_t*)(RAM_BIOS_ADDR + i) = 0;}   
  #endif

  #if defined(RAM_APPL_DATA)       
  //toto je oblast pameti v APPL, ktera neni sdilena  
  for(i = 0; i < RAM_APPL_DATA_SIZE; i+=4){ *(uint32_t*)(RAM_APPL_DATA_ADDR + i) = 0;} 
  #endif  
  
  #if defined(RAM_APPL)
  //toto je oblast pameti v APPL, ktera je adresovana absolutne
  for(i = 0; i < RAM_APPL_SIZE; i+=4){ *(uint32_t*)(RAM_APPL_ADDR + i) = 0;}   
  #endif 

  #if defined(RAM_EXPAND)
  //toto je oblast pameti v APPL, ktera je adresovana absolutne
  for(i = 0; i < XRAM_APPL_SIZE; i+=4){ *(uint32_t*)(XRAM_APPL_ADDR + i) = 0xFFFFFFFF;}   
  #endif 
  }

static void init_mcu(void){
  //pro Analog se pin nenastavuje - standartne je jako vstupni
  RCC->AHBENR = 0
  | RCC_AHBENR_DMA1EN;                  // DMA1 clock enable
  
  RCC->APB1ENR = 0
  | RCC_APB1ENR_TIM2EN                  // Timer 2 clock enable (casovani RS485)
  | RCC_APB1ENR_TIM3EN                  // Timer 3 clock enable pro PWMku (BEEPER) 
  | RCC_APB1ENR_TIM4EN                  // Timer 4 clock enable spousteni AD prevodniku  
  | RCC_APB1ENR_TIM7EN                  // Timer 7 clock enable BEEPER (casovani) 
  | RCC_APB1ENR_DACEN                   // DAC interface clock enable 
  | RCC_APB1ENR_SPI2EN                  // SPI 2 clock enable (LED driver)   
  | RCC_APB1ENR_USART2EN                // USART2 clock enable        
  | RCC_APB1ENR_USART3EN                // USART3 clock enable
  | RCC_APB1ENR_I2C1EN                  // I2C 1 clock enable (WIFI driver)       
  | RCC_APB1ENR_PWREN                   // Power interface clock enable pro RTC         
  | RCC_APB1ENR_BKPEN;                  // Backup interface clock enable - uzivatelske registry v RTC
  
  RCC->APB2ENR = 0                      // Most of the peripherals are connected to APB2. Turn on the clocks for the interesting peripherals 
  | RCC_APB2ENR_AFIOEN                  // Alternate Function I/O clock enable - peripheral
  | RCC_APB2ENR_IOPAEN                  // I/O port A clock enable
  | RCC_APB2ENR_IOPBEN                  // I/O port B clock enable  
  | RCC_APB2ENR_IOPCEN                  // I/O port C clock enable
  | RCC_APB2ENR_IOPDEN                  // I/O port D clock enable 
  | RCC_APB2ENR_ADC1EN                  // ADC 1 interface clock enable 
  | RCC_APB2ENR_USART1EN;               // USART1 clock enable  
  
  AFIO->MAPR = 0         
  | AFIO_MAPR_TIM3_REMAP
  | AFIO_MAPR_USART3_REMAP_PARTIALREMAP        
  | AFIO_MAPR_SWJ_CFG_JTAGDISABLE; 
  
  GPIOB->ODR = 0  
  | GPIO_ODR_ODR14;                     // Pull-up on pin - wifi fabric init
  
  GPIOA->ODR = 0  
  | GPIO_ODR_ODR12;                     // Pull-up on pin - PROG
  
  GPIOA->CRL = 0
  | GPIO_CRL_CNF0_0                     // Input floating - AN8 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)
  | GPIO_CRL_CNF1_0                     // Input floating - AN1 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)         
  | GPIO_CRL_MODE2_0                    // Max output speed 10MHz - wifi USART2 TXD   
  | GPIO_CRL_CNF2_1                     // Alternate output with Push-pull - wifi USART2 TXD 
  | GPIO_CRL_CNF3_1                     // Input with pull-up - wifi USART2 RXD          
  | GPIO_CRL_MODE4_0                    // Max output speed 10MHz - DAC channel 1
  | (GPIO_CRL_CNF4_0|GPIO_CRL_CNF4_1)   // Alternate output open-drain - DAC channel 1
  | GPIO_CRL_MODE5_0                    // Max output speed 10MHz - DAC channel 2
  | (GPIO_CRL_CNF5_0|GPIO_CRL_CNF5_1)   // Alternate output open-drain - DAC channel 2
  | GPIO_CRL_CNF6_0                     // Input floating - AN4 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)
  | GPIO_CRL_CNF7_0;                    // Input floating - AN6 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)  
  
  GPIOA->CRH = 0             
  | GPIO_CRH_MODE8_0                    // Max output speed 10MHz - OUT 2         
  | GPIO_CRH_MODE9_0                    // Max output speed 10MHz - debug USART1 TXD   
  | GPIO_CRH_CNF9_1                     // Alternate output with Push-pull - debug USART1 TXD 
  | GPIO_CRH_CNF10_1                    // Input with pull-up - debug USART1 RXD   
  | GPIO_CRH_MODE11_0                   // Max output speed 10MHz - USART3 DIR RS485 
  | GPIO_CRH_CNF12_1                    // Input with pull-up - PROG        
  | GPIO_CRH_MODE15_0;                  // Max output speed 10MHz - WIFI RST      
  
  GPIOB->CRL = 0   
  | GPIO_CRL_CNF0_0                     // Input floating - AN2 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)
  | GPIO_CRL_CNF1_0                     // Input floating - AN9 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)        
  | GPIO_CRL_MODE3_0                    // Max output speed 10MHz - WIFI BOOT    
  | GPIO_CRL_MODE4_0                    // Max output speed 10MHz - STROBE (LED driver)  
  | GPIO_CRL_MODE6_0                    // Max output speed 10MHz on Pin 6 
  | (GPIO_CRL_CNF6_0|GPIO_CRL_CNF6_1)   // Alternate output open-drain - I2C SCL
  | GPIO_CRL_MODE7_0                    // Max output speed 10MHz on Pin 7       
  | (GPIO_CRL_CNF7_0|GPIO_CRL_CNF7_1);  // Alternate output open-drain - I2C SDA    
  
  GPIOB->CRH = 0    
  | GPIO_CRH_MODE8_0                    // Max output speed 10MHz - OUT 8 
  | GPIO_CRH_MODE9_0                    // Max output speed 10MHz - OUT 7 
  | GPIO_CRH_MODE12_0                   // Max output speed 10MHz - OUT 3        
  | GPIO_CRH_MODE13_0                   // Max output speed 10MHz on Pin 13 - SPI2 SCK          
  | GPIO_CRH_CNF13_1                    // Alternate output with Push-pull - SPI2 SCK  
  | GPIO_CRH_CNF14_1                    // Input with pull-up - wifi fabric init        
  | GPIO_CRH_MODE15_0                   // Max output speed 10MHz on Pin 15 - SPI2 SI         
  | GPIO_CRH_CNF15_1;                   // Alternate output with Push-pull - SPI2 SI        
  
  GPIOC->CRL = 0  
  | GPIO_CRL_CNF3_0                     // Input floating - AN7 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)
  | GPIO_CRL_CNF4_0                     // Input floating - AN5 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)
  | GPIO_CRL_CNF5_0                     // Input floating - AN3 (umozni cist AD i LOG hodnotu vstupu - rychle an vstupy)
  | GPIO_CRL_MODE6_0                    // Max output speed 10MHz - PWM 
  | GPIO_CRL_CNF6_1                     // Alternate output with Push-pull - PWM
  | GPIO_CRL_MODE7_0;                   // Max output speed 10MHz - OUT 4         
  
  GPIOC->CRH = 0      
  | GPIO_CRH_MODE8_0                    // Max output speed 10MHz - OUT 6 
  | GPIO_CRH_MODE9_0                    // Max output speed 10MHz - OUT 5         
  | GPIO_CRH_MODE10_0                   // Max output speed 10MHz - debug USART3 TXD   
  | GPIO_CRH_CNF10_1                    // Alternate output with Push-pull - debug USART3 TXD 
  | GPIO_CRH_CNF11_1                    // Input with pull-up - debug USART3 RXD
  | GPIO_CRH_MODE12_0;                  // Max output speed 10MHz - USART1 DIR RS485
  
  GPIOD->CRL = 0   
  | GPIO_CRL_MODE2_0;                   // Max output speed 10MHz - OUT 1     
  }

void SysTick_Handler(void){
  static uint8_t c_5ms = 0;
  static uint8_t c_100ms = 0;
  static uint8_t c_500ms = 0;
  static uint8_t m_1s = 0;
  
  p_bios_fce_DRIVE_SERVO_X jump_drive_servo;
  
  bram_m_global_cas |= (1<<C_1MS_LED);
  if(c_5ms != 0){c_5ms--;}  
  else{
    c_5ms = TIMER_MS(5,1);
    bram_m_global_cas |= (1<<C_5MS_USER_1)|(1<<C_5MS_USER_2);

    if(c_100ms != 0){c_100ms--;}
    else{    
      c_100ms = TIMER_MS(100,5);
      bram_m_global_cas |= (1<<C_100MS_WIFI)|(1<<C_100MS_USER_1)|(1<<C_100MS_USER_2);   
      
      if(bram_fce_DRIVE_SERVO1 != 0xFFFFFFFF){
        jump_drive_servo = (p_bios_fce_DRIVE_SERVO_X)bram_fce_DRIVE_SERVO1;  
        jump_drive_servo();      
        }
      
      if(bram_fce_DRIVE_SERVO2 != 0xFFFFFFFF){
        jump_drive_servo = (p_bios_fce_DRIVE_SERVO_X)bram_fce_DRIVE_SERVO2;  
        jump_drive_servo();
        }

      if(c_500ms != 0){c_500ms--;}
      else{
        c_500ms = TIMER_MS(500,100);   
        bram_m_global_cas |= (1<<C_500MS_USER_1)|(1<<C_500MS_USER_2);

        m_1s = ~m_1s;
        if(m_1s){
          bram_m_global_cas |= (1<<C_1S_ADC_DELAY)|(1<<C_1S_USER_1)|(1<<C_1S_USER_2);
          }
        }
      }
    }
  }

Používám startup soubor staženej ze stránek st.com. V něm se provádí inicializace automatických promených a nastavení hodin MCU.
Pro mě je nejdůležitější volání funkce init_mcu(), kde nastavuju periferie procesoru jaké budou v programu použity. Samotné nastavení periferií provádím až v konkrétních modulech viz ukázka knihovny na ovládání beepru.

#include <stdint.h>
#include <memory_map_bios.h>
#include <stm32f10x.h>
#include <beeper.h>

static uint16_t c_beep;
static uint16_t *p_melody;

const static uint16_t melodie_start] = {                                       //piskani pri zapnuti napajeni
  TON_OFF, BMS(100),
  TON_E  , BMS(100),
  TON_OFF, BMS(100),
  TON_F  , BMS(100),
  TON_OFF, BMS(100),
  TON_E  , BMS(100),
  0xFFFF,         
  };

void init_beeper(void){
  TIM3->PSC = TON_OFF;                                                          // Set prescaler to 0 (PSC + 1)
  TIM3->ARR = 16 - 1;                                                           // Auto reload value - dela rozsah  
  TIM3->CCR2 = 0;                                                               // Duty cycle

  TIM3->CCMR1 = 0  
  | TIM_CCMR1_OC1PE                                                             // Enable CCR1 preloading
  | TIM_CCMR1_OC1M_2                                                            // PWM mode 1: OC1M = 110  
  | TIM_CCMR1_OC1M_1; 
  
  TIM3->CCER = 0
  | TIM_CCER_CC1E;                                                              // Enable Capture/Compare output 2
  
  TIM3->CR1 = 0        
  | TIM_CR1_CEN; 
  
  BEEP_OFF;  
  
  // Set prescaler
  TIM7->PSC = 132;
  TIM7->CNT = 0;
  TIM7->DIER = TIM_DIER_UIE;                                                    // Enable update interrupt (timer level)
  NVIC_EnableIRQ(TIM7_IRQn);                                                    // Enable interrupt from TIM7 (NVIC level) 
  
  c_beep = 0;
  *p_melody = 0;
  
  start_melody((uint32_t)melodie_start);                                        //uvodni piskani
  }

//=============================================================================
// TIM7 Interrupt Handler
//=============================================================================
void TIM7_IRQHandler(void){                                                     
  if(TIM7->SR & TIM_SR_UIF){                                                    // if UIF flag is set   
    TIM7->SR &= ~TIM_SR_UIF;                                                    // clear UIF flag
    if(*(p_melody + (c_beep+=2)) == 0xFFFF){
      TIM7->CR1 &= ~TIM_CR1_CEN;
      TIM3->PSC = TON_OFF;
      BEEP_OFF;
      return;
      }    
    if(*(p_melody + c_beep) == 0xFFFE){ c_beep = 0;}
    else{TIM3->PSC = *(p_melody + c_beep);}  
    TIM7->ARR = *(p_melody + c_beep + 1);
    }
  }

void start_melody(uint32_t melody){  
  if((TIM7->CR1 & TIM_CR1_CEN) != 0){return;}
  else{
    BEEP_ON;
    c_beep = 0;
    p_melody = (uint16_t*)melody;
    TIM3->PSC = *(p_melody + 0); 
    TIM7->ARR = *(p_melody + 1);
    TIM7->CR1 |= TIM_CR1_CEN;                                                   // Enable timer
    }
  }

Jinak co se týče toho, že se ti nezdá jednoduchá cesta pochopit princip fungování maker a struktur tak si upřímně myslim že jsi tatar…když jsem z stmkem začínal rozhodněj jsem netvrdil, že umim dobře programovat atmel, ale kontrétně tohle mi přišlo naprosto intuitivní. ST má jedny z nejlepších tadasheetu k MCU co jsem kdy viděl.

Dík moc za kód. Snad se v něm zorientuju. Nevypadá to složitě. Asi už tuším jak na to.
Dost mě asi naučí i STM32CubeMX. Neříkám, že v něm budu tvořit kompletně, ale mnohé mi předvede jak se správně inicializuje.
Kdybych něco nepobral tak se ještě ozvu.