sobota, 21 czerwca 2014

STM32F429i - PWM + LED

Witajcie!
Jako kontynuację artykułów o STM32F429i przedstawię sposób płynnej
regulacji świecenia diodą led.
Do naszej zabawy będziemy potrzebować przejściówkę FT232, ponieważ
zmodyfikujemy nasz terminal, który wspomoże nas w płynnej regulacji
wypełnienia PWM, oraz diodę led wraz z rezystorem około 300 ohm.

PWM - czyli Pulse Width Modulation -  jest to modulacja szerokości impulsu
utworzonego np: na "wyjściu" naszego timera, który jest tak skonfigurowany aby
generował sygnał prostokątny. My zaś zmieniając parametry modulujemy
tzw. wypełnienie czyli szerokość, tym samym zmieniając wartość średnią
napięcia. Oczywiście nie możemy zapomnieć o bardzo ważnym parametrze
jakim jest częstotliwość naszego przebiegu prostokątnego. Częstotliwość tą
ustawiłem na 1kHz.
Dla lepszego zobrazowania o co chodzi przedstawię screeny z oscyloskopu,
w którym zmienię wartość wypełnienia co z kolei wpłynie na wartość średnią
napięcia na pinie mikrokontrolera.

PWM - 25% -Uavg = 768mV  f= 1kHz
PWM - 50% -Uavg = 1,5V     f= 1kHz

PWM - 75% -Uavg = 2.25V     f= 1kHz


Na oscylogramach widać ładnie jak zmienia się wypełnienie i tym samym wartość
średnia napięcia.
Oczywiście PWM możemy regulować np: prędkość obrotową silnika, nic nie stoi na
przeszkodzie :). W kolejnych cyklach pokażemy sobie jak sterować serwami 
modelarskimi :) co przydaje się każdemu elektronikowi chcącemu zbudować własnego
robota.

Ok najpierw zajmijmy się kodem C# naszego terminala ponieważ dodałem kontrolkę
TrackBara, którą regulujemy płynnie wypełnienie.



 private void trackBar1_Scroll(object sender, EventArgs e)
        {
            serialPort1.Write(trackBar1.Value.ToString() + "\r");
        }
Wygląd naszej formatki powinien wyglądać w taki sposób:

Widzimy że kod jest banalny. W zdarzeniu event_scroll TrackBara za każdym przesunięciem 
wskaźnika wysyłamy wartość na COM od 0 - 512.

Ok to teraz przejdźmy do trudniejszych rzeczy. Przedstawię kod pliku main.c

#include "main.h"

#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_tim.h"
#include "stm32_ub_uart.h"
#include "stdlib.h"


void Init(void);
void Delay(volatile uint32_t nCount);
void initPWM(void);

void strreverse(char* begin, char* end);


int main(void)
{
  UART_RXSTATUS_t check;
  char rx_buf[RX_BUF_SIZE];


  SystemInit(); 
  Init();   //inicjalizacja diody - do tego ćwiczenia nie potrzebna
  initPWM();  //inicjalizacja timera 5, który pracuje w trybie PWM
  UB_Uart_Init();   //inicalizacja USARTA

  uint16_t i = 0;

  while(1)
  {
  check=UB_Uart_ReceiveString(COM1,rx_buf); //zapisujemy odebrane dane do bufora

  if(check==RX_READY) {  //sprawdzamy czy dane zostały odebrane 
      
    i = atol(rx_buf);  //konwertujemy dane z bufora czyli ascii na int
       TIM_SetCompare1(TIM5, i); //timer pwm - zapisujemy odebraną wartosc już skonwertowaną do timera

       UB_UART_SendInt(COM1,i,10); //wysylamy spowrotem odebraną wartosc
       //UB_Uart_SendString(COM1,rx_buf); 
       UB_Uart_SendString(COM1,"\r\n"); //wysylamy znak powrotu karetki i nowej lini
  }
       //Delay(5000000);


  }
}

void initPWM()
{
 /* TIM5 włączenie zegara */
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);

 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

 /* Obliczenie wartoci preskalera */
 uint16_t u16PrescalerValue = (uint16_t) ((SystemCoreClock) / 1000000)- 1;
 TIM_TimeBaseStructure.TIM_Prescaler = u16PrescalerValue; // 1MHz - preskaler
 TIM_TimeBaseStructure.TIM_Period = 500;                  // 1 MHz / 500 /2  = 1kHz (1ms)
 TIM_TimeBaseStructure.TIM_ClockDivision = 0;
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //licznik zlicza w góre

 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //inicjalizacja struktury Timera 5

 TIM_OCInitTypeDef TIM_OCInitStructure;

 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  //tryb pwm
 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //odpalenie timera
 TIM_OCInitStructure.TIM_Pulse = 0; 
 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

 /*********************TIM5**************************/
 /* konfiguracja trybu PWM: kanał 1 (GPIOA Pin 0)*/
 TIM_OC1Init(TIM5, &TIM_OCInitStructure);
 TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable );
        TIM_Cmd(TIM5, ENABLE); //odpalenie timera

 ///////////////konfuguracja GPIO ///////////////
 GPIO_InitTypeDef GPIO_PWM;

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

 GPIO_PWM.GPIO_Mode = GPIO_Mode_AF;
 GPIO_PWM.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_PWM.GPIO_OType = GPIO_OType_PP;
 GPIO_PWM.GPIO_PuPd = GPIO_PuPd_UP;

 //PORT A Pin0
 GPIO_PWM.GPIO_Pin = GPIO_Pin_0;
 GPIO_Init(GPIOA, &GPIO_PWM);

 GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5 );

Myślę, że komentarze pomogą w zrozumieniu kodu.

Na koniec zostaje nam pokazanie jak wszystko wspólnie działa. Na filmie będzie
ładnie widać jak płynnie zmienia się wartość PWM a dioda led czerwona zmienia
jasność.

Przyjemnej zabawy :)

poniedziałek, 16 czerwca 2014

C# - Baza Danych (edit) - cz.4

Witajcie!
Dzisiaj będzie kontynuacja wątku o bazie danych. Pokażemy sobie jak zrobić możliwość
edycji naszych danych.

No to do dzieła!
Przeciągamy z ToolBoxa button i zmieniamy mu "content" w kodzie XAML na Edytuj,
oraz "Name" na "btnEdit".

Ok. jeśli dołożyliśmy nasz button i zmieniliśmy jego parametry, to klikamy dwa razy
na niego i przechodzi do zdarzenia kliknięcia.
Tam stworzymy nowy obiekt klasy Baza oraz wywołamy metodę "EditaData" do, której
przekażemy parametry takie jak: nazwę naszej kontrolki DataGridView, numer wiersza do
edycji, nazwę naszego textBoxa z imieniem, oraz nazwę textBoxa z nazwiskiem.
        private void btnEdit_Click(object sender, RoutedEventArgs e)
        {
            Baza b1 = new Baza(@"Data Source = Baza.sdf");
            b1.EditData(dg, numerWiersza, txtImie.Text, txtNazwisko.Text);
            MessageBox.Show("Edytowano");
            b1.UpdateGrid(dg);
        }
Ok. Jak widać dzięki temu że cały kod metody do edycji jest zawarty w naszej klasie
to w zdarzeniu nasz kod sprowadza się raptem do 4 linijek.
Co my tu mamy?.
Tworzymy nowy obiekt klasy Baza gdzie w konstruktorze przekazujemy ścieżkę
do naszej bazy danych. Następnie wywołujemy metodę, która woła aby jej zapodać
nazwę naszej kontrolki DataGridView, numer wiersza jaki został wybrany mychą,
textBox w który wpisujemy Imię oraz drugi z Nazwiskiem. Ostatnia linijka to funkcja
która robi update naszej kolekcji w gridzie.

Ok teraz przejdźmy do naszej klasy Baza.
W kodzie naszej klasy dodajemy metodę EdytujDane, której nadajemy atrybut public
oraz nie zwracamy nic czyli typ void.
public void EditData(DataGrid dg, int RowNumber, string imie, string nazwisko)
        {
            SqlCeConnection cn = new SqlCeConnection(_conn);
            da = new SqlCeDataAdapter();
           

            da.UpdateCommand = new SqlCeCommand("UPDATE Pracownicy SET Imie=@Imie, Nazwisko=@Nazwisko WHERE ID = @ID", cn);
            da.UpdateCommand.Parameters.Add("@Imie", SqlDbType.NVarChar).Value = imie;
            da.UpdateCommand.Parameters.Add("@Nazwisko", SqlDbType.NVarChar).Value = nazwisko;
            da.UpdateCommand.Parameters.Add("@ID",SqlDbType.Int).Value = RowNumber;

            cn.Open();
            da.UpdateCommand.ExecuteNonQuery();
            cn.Close();
        }

W pierwszej kolejności tworzymy obiekt cn, gdzie w konstruktorze przekazujemy string
połączenia z bazą, również tworzymy obiekt typu DataAdapter - obiekt ten reprezentuje zestaw
poleceń do połączenia z bazą danych jak również manipulacji na tychże danych.
W następnej linijce wywołujemy zapytanie query do naszej bazy danych, gdzie ustawiamy
do Imienia przypisujemy zmienną Imię a do Nazwiska nazwisko.
W następnych linijka jest do naszych edytowanych wierszy przekazywana wartość imienia i nazwiska
które są parametrem metody. dalej otwieramy połączenie, wykonujemy polecenie query i natychmiast
połączenie to zamykamy.

 Zrobiłem jeszcze małą modyfikację poprzednich metod, wywaliłem z tamtą odświeżanie
grida i zrobiełm z tego metodę, którą wywołujemy po dodaniu, usunięciu czy edycji danych.
 public void UpdateGrid(DataGrid dg)
        {
            da = new SqlCeDataAdapter();
            dt = new DataTable();

            dg.ItemsSource = dt.DefaultView;
            da.Update(dt); //update grida
        }

Całość teraz wygląda tak:

Kod klasy Baza.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Data;
using System.Windows;
using System.Windows.Controls;


namespace WPF_Baza
{
    public class Baza
    {
        private static string _conn;
                
        
        public SqlCeDataAdapter da;
        public DataTable dt;
        

        /// 
        /// konstruktor
        /// 
        /// 
        public Baza(string conn)
        {
            _conn = conn;
        }       
        
        /// 
        /// dodawanie danych do bazy
        /// 
        public void InsertData(string Imie, string Nazwisko)
        {
            SqlCeConnection cn = new SqlCeConnection(_conn);

            da = new SqlCeDataAdapter();
            da.InsertCommand = new SqlCeCommand("INSERT INTO Pracownicy(Imie, Nazwisko) VALUES(@Imie, @Nazwisko)", cn);
            da.InsertCommand.Parameters.Add("@Imie", SqlDbType.NVarChar).Value = Imie;
            da.InsertCommand.Parameters.Add("@Nazwisko", SqlDbType.NVarChar).Value = Nazwisko;

            cn.Open();      
            da.InsertCommand.ExecuteNonQuery();
            cn.Close();
            
            MessageBox.Show("Dodano do bazy!");
        }
        /// 
        /// wyświetlanie danych z bazy
        /// 
        public void ShowData(DataGrid dg)
        {
            SqlCeConnection cn = new SqlCeConnection(_conn);
            dt = new DataTable();
            da = new SqlCeDataAdapter();
            da.SelectCommand = new SqlCeCommand("SELECT * FROM Pracownicy", cn);
            dt.Clear();
            da.Fill(dt);
            dg.ItemsSource = dt.DefaultView;
            da.Update(dt);
        }
        /// 
        /// usuwanie danych z bazy
        /// 
        public void DeleteData(DataGrid dg, int RowNumber)
        {
            SqlCeConnection cn = new SqlCeConnection(_conn);
            da = new SqlCeDataAdapter();
            
            da.DeleteCommand = new SqlCeCommand("DELETE FROM Pracownicy WHERE ID = @ID", cn);
            da.DeleteCommand.Parameters.Add("@ID", SqlDbType.Int).Value = RowNumber; //usunięcie numeru wiersza przekazanego jako parametr do metody

            cn.Open();
            da.DeleteCommand.ExecuteNonQuery();
            cn.Close();            
        }
        public void EditData(DataGrid dg, int RowNumber, string imie, string nazwisko)
        {
            SqlCeConnection cn = new SqlCeConnection(_conn);
            da = new SqlCeDataAdapter();
           

            da.UpdateCommand = new SqlCeCommand("UPDATE Pracownicy SET Imie=@Imie, Nazwisko=@Nazwisko WHERE ID = @ID", cn);
            da.UpdateCommand.Parameters.Add("@Imie", SqlDbType.NVarChar).Value = imie;
            da.UpdateCommand.Parameters.Add("@Nazwisko", SqlDbType.NVarChar).Value = nazwisko;
            da.UpdateCommand.Parameters.Add("@ID",SqlDbType.Int).Value = RowNumber;

            cn.Open();
            da.UpdateCommand.ExecuteNonQuery();
            cn.Close();
        }
        public void UpdateGrid(DataGrid dg)
        {
            da = new SqlCeDataAdapter();
            dt = new DataTable();

            dg.ItemsSource = dt.DefaultView;
            da.Update(dt); //update grida
        }
    }
}


Ok to by było na tyle w tym odcinku.
 Jeśli będziecie mieć jakieś pytania to śmiało piszcie!!

piątek, 6 czerwca 2014

STM32F429i-Discovery USART_LED_TOG + C#

Witajcie!
W ostatnim odcinku o STM32F429i opowiedzieliśmy sobie jak odpalić uarta
na naszej płytce. Dzisiaj chciałbym troszkę zmodyfikować nasz kod a raczej
dodać do niego mruganie diodami, ale nie za pomocą micro-switcha a zrobić
sterowanie za pomocą naszego terminala, w którym dodamy sobie taką możliwość.
Będziemy wysyłać literkę przez port COM na naszą płytkę co spowoduje
zapale się diody.
Oczywiście nie użyjemy tam żadnych sztucznych opóźnień. Zrobimy to tak jak
należy:).
Kod to mrugania diody oczywiście znajduje się w jednym z poprzednich postów
wprowadzających do opisu naszej płytki STM32F429i i to ten kod użyjemy.

Ale najpierw uzbroimy nasz terminal w możliwość wysyłania własnych
znaków (stringów) przez port COM.

Jeszcze zanim dodamy przyciski do wysyłania naszych znaków, wstawimy
sobie buttonik do czyszczenia terminalu, tego w sumie mi brakowało
podczas pracy z tym terminalem.
A więc metoda drag&drop przeciągamy na naszą formatkę z ToolBoxa
buton i zmieniamy mu nazwę na "btnClear" oraz nazwę, która będzie wyświetlana
na nim na "Wyczyść".
Klikamy ppm na buttonie i przechodzimy do zdarzenia kliknięcia, w którym
napiszemy kod do wyczyszczenia tekstu z "richTextBox1".

private void btnClear_Click(object sender, EventArgs e)
{
    richTextBox1.Clear(); //czyszczenie richtexboxa
}

Teraz możemy sobie dodać dwa buttony do sterowania diodami na naszej
płytce.
Nazwiemy odpowiednio "btnRed i btnGreen", jak nazwa wskazuje jeden będzie
odpowiadał za mruganie diodą zieloną a drugi czerwoną.
Aby nasze butony ładnie wyglądały po odpaleniu naszego terminala
zmienimy im kolory.
W zdarzeniu Form1_Load dodajemy kod:
private void Form1_Load(object sender, EventArgs e)
{
    btnGreen.BackColor = Color.Green;
    btnRed.BackColor = Color.Red;
}

Podczas ładowania okna kolor tła naszych przycisków będzie miał odpowiednio
zielony i czerwony.
Wstępnie terminal powinien wyglądać tak:

OK. Jeśli mamy dodane nasze butony to oprogramujemy sobie ich zdarzenia kliknięcia.
 private void btnRed_Click(object sender, EventArgs e)
 {
        serialPort1.WriteLine("r\n\r");
 }

 private void btnGreen_Click(object sender, EventArgs e)
 {
        serialPort1.WriteLine("g\n\r");
 }

Generalnie jeśli chodzi o nasz terminal to będzie na dzień dzisiejszy tyle modyfikacji.
W przyszłości dodamy sobie możliwości wysyłania stringów zdefiniowanych przez nas,
co będzie bardziej uniwersalnym rozwiązaniem.

Ok teraz przejdziemy do kodu w C jaki zapodamy do naszego procka:
 

#include "main.h"
#include "stm32_ub_uart.h"

void Diody_Init(void);
void Delay(volatile uint32_t nCount);

int main(void)
{
  UART_RXSTATUS_t check;
  char rx_buf[RX_BUF_SIZE];

  SystemInit();

  // inicjalizacja UART (COM1 : TX=PA9, RX=PA10)
  UB_Uart_Init();
  Diody_Init();

  while(1)
  {
       check=UB_Uart_ReceiveString(COM1,rx_buf);

       if(check==RX_READY) {

      if(rx_buf[0] == 'g')
      {
        GPIOG->ODR ^= GPIO_Pin_13; // led_tog red
      }else if(rx_buf[0] == 'r')
      {
        GPIOG->ODR ^= GPIO_Pin_14; // led_tog green
      }

    }

  }
}
void Diody_Init(void) {
  GPIO_InitTypeDef GPIO_InitStructure;

  // włączenie zegara
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); //ustawienie zegara

  // ustawienie pinów portu G
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOG, &GPIO_InitStructure);
}

void Delay(volatile uint32_t nCount) { // funkcja opóźniająca
  while(nCount--)
  {
  }
}


Myślę że kod jest czytelny i nie wymaga jakiegoś większego tłumaczenia.
W pętli głównej odbieramy literkę "r lub g" i po odebraniu po prostu zmieniamy
stan naszych diod na przeciwny.

Jeszcze krótki film pokazujący działanie naszego terminala i mruganie diodkami
na Discovery.

Link do pliku exe terminala.

Przyjemnej zabawy