Muhteşem non-block stm32 i2c 1602 karakter lcd kütüphanesi

semih_s

Hobici
Katılım
16 Aralık 2020
Mesajlar
2,113
Aziz ve muhterem kardeşlerim! Karşınızda naçizane elektronik atölyemin, insanlığın kolektif birikimine son katkısı olan ve dünyada hala güzel şeyler olabileceğine olan inancınızı pekiştirecek eserim(!)

Bu kütüphaneyi aslında 16 bit i2c expander ile karakter LCD ekranları 8 bit modda kullanmak için geçen yıl yazmıştım. Hızlı çalışan, en önemlisi non-block çalışan bir kütüphane. Şimdi aylak kalınca uğraşmaya geri döndüm. Bu 4 bit modda çalışan versiyonu ve biraz daha hızlı çalışması için yz desteği de aldım. Bence gayet iyi oldu. Ama kütüphaneyi henüz etraflıca test etmedim.

Aslında burada özel bir şey yok. Sadece lcd veri transferinin işlemciyi bloklamadan çalışabilmesi için i2c'yi DMA desteğiyle kullanmak var. Bununla birlikte float ve double değişkenleri de mümkün olan en hızlı şekilde ascii'ye çevirmeye çalıştım.

Kütüphanedeki "lcd_tarama()" fonksiyonu her çağırıldığında sıradaki tek karakteri lcd'ye göndermek için i2c periperal'e dma ile çalışan bir dizi değişken gönderir. kütüphanedeki fonksiyonlar lcd ile iletişim kurmaz, sadece acii çevrimini yapar ve ekranı temsil eden iki boyutlu bir seride yerine yazar. "lcd_tarama" fonksiyonu bu diziyi kullanarak çalışır.

Şu anda basitçe bir i2c periperali ele geçirerek çalışıyor. Bu tarama fonksiyonu, bütün karakterleri yazdırınca durup, güncelleme gelince çalışacak şekilde uygulanabilir. şu haliyle her 4 çağrıda 1 karakter yazdırıyor. Yani döngüde her milisaniye çağrılırsa ekran güncelleme hızı 32 karakter ve ilk satır çağrılarıyla birlikte 7.35 Hz oluyor. Kütüphane üniversal değil. sadece 1602 ile çalışıyor. Ama yz uşağınız bunu kolayca diğer , 4002,2004, 0802 formatlarla çalışacak hale getirir :) .

i2c-lcd-4bit.c :
C:
/**
 * PCF8574 i2c-lcd sürücüsü - 4 bit mod
 * STM32F103
 *
 * Pinout:
 *      LCD         PCF8574
 *      D4..D7      P4...P7
 *      E           P2
 *      R/W         P1
 *      RS          P0
 *      BACKLIGHT   P3
 *
 **/

#include "i2c-lcd-4bit.h"
#include "main.h"

extern I2C_HandleTypeDef hi2c1;


static uint8_t i2c_s   = 0;
static uint8_t i2c_pas = 0;  // 0: üst nibble E-high
                               // 1: üst nibble E-low
                               // 2: alt nibble E-high
                               // 3: alt nibble E-low
static uint8_t lcd_en  = 0;
static uint8_t b_Light = BL;
static uint8_t data_t;
static uint8_t cur_char;
static uint8_t cur_rs;

uint8_t cur[2] = {0, 0};

char satir[2][20] = {{"                "}, {"                "}};

// Türkçe özel karakterler
uint8_t ch0[8] = {0, 14, 16, 14, 1, 30, 4, 0};    // ş/Ş
uint8_t ch1[8] = {0, 14, 17, 16, 17, 14, 4, 0};   // ç/Ç
uint8_t ch2[8] = {0, 0, 12, 4, 4, 4, 14, 0};      // ı
uint8_t ch3[8] = {4, 0, 14, 4, 4, 4, 14, 0};      // İ
uint8_t ch4[8] = {14, 0, 14, 18, 14, 2, 28, 0};   // ğ
uint8_t ch5[8] = {14, 0, 14, 16, 22, 18, 14, 0};  // Ğ

// ============================================================
//  DONANIM KATMANI - PCF8574, 4-bit mod
// ============================================================

static inline void send_nibble_poll(uint8_t nibble, uint8_t rs)
{
    uint8_t d;
    d = (nibble & 0xF0) | b_Light | E | (rs ? RS : 0);
    HAL_I2C_Master_Transmit(&hi2c1, ADRES_LCD, &d, 1, 100);
    d &= ~E;
    HAL_I2C_Master_Transmit(&hi2c1, ADRES_LCD, &d, 1, 100);
}

static inline void lcd_send_cmd_poll(uint8_t cmd)
{
    send_nibble_poll(cmd & 0xF0, 0);
    HAL_Delay(1);
    send_nibble_poll((cmd << 4) & 0xF0, 0);
    HAL_Delay(1);
}

static void lcd_send_data_poll(uint8_t data)
{
    send_nibble_poll(data & 0xF0, 1);
    send_nibble_poll((data << 4) & 0xF0, 1);
}

// Non-blocking DMA gönderici - her çağrıda bir adım ilerler
static inline void lcd_send_data(uint8_t rs)
{
    switch(i2c_pas)//bir karakter ancak 4  sekansta gönderilir
    {
        case 0:  // üst nibble E-high
            cur_rs = rs;
            data_t = (cur_char & 0xF0) | b_Light | E | (rs ? RS : 0);
            HAL_I2C_Master_Transmit_DMA(&hi2c1, ADRES_LCD, &data_t, 1);
            i2c_pas++;
            break;
        case 1:  // üst nibble E-low
            data_t &= ~E;
            HAL_I2C_Master_Transmit_DMA(&hi2c1, ADRES_LCD, &data_t, 1);
            i2c_pas++;
            break;
        case 2:  // alt nibble E-high
            data_t = ((cur_char << 4) & 0xF0) | b_Light | E | (cur_rs ? RS : 0);
            HAL_I2C_Master_Transmit_DMA(&hi2c1, ADRES_LCD, &data_t, 1);
            i2c_pas++;
            break;
        case 3:  // alt nibble E-low
            data_t &= ~E;
            HAL_I2C_Master_Transmit_DMA(&hi2c1, ADRES_LCD, &data_t, 1);
            i2c_pas = 0;
            break;
    }
}


void lcd_tarama(void)
{
    if(hi2c1.State != HAL_I2C_STATE_READY) return;
    if(!lcd_en) return;

    if(i2c_pas == 0)
    {
        if     (i2c_s == 0)       cur_char = 0x80;
        else if(i2c_s < 17)       cur_char = satir[0][i2c_s - 1];
        else if(i2c_s == 17)      cur_char = 0xC0;
        else if(i2c_s < 34)       cur_char = satir[1][i2c_s - 18];
    }

    uint8_t rs = (i2c_s == 0 || i2c_s == 17) ? 0 : 1;
    lcd_send_data(rs);

    if(i2c_pas == 0)
    {
        i2c_s++;
        if(i2c_s == 34) i2c_s = 0;
    }
}


void lcd_init(void)
{
    HAL_Delay(50);

    // 4-bit moda geçiş sekansı
    send_nibble_poll(0x30, 0); HAL_Delay(5);
    send_nibble_poll(0x30, 0); HAL_Delay(1);
    send_nibble_poll(0x30, 0); HAL_Delay(1);
    send_nibble_poll(0x20, 0); HAL_Delay(1);  // 4-bit moda geç

    lcd_send_cmd_poll(0x28); // 4-bit, 2 satır, 5x8
    lcd_send_cmd_poll(0x08); // Display off
    lcd_send_cmd_poll(0x01); // Clear
    HAL_Delay(2);
    lcd_send_cmd_poll(0x06); // Entry mode
    lcd_send_cmd_poll(0x0C); // Display on

    createChar(0, ch0);
    createChar(1, ch1);
    createChar(2, ch2);
    createChar(3, ch3);
    createChar(4, ch4);
    createChar(5, ch5);
}

void createChar(uint8_t location, uint8_t charmap[])
{
    location &= 0x7;
    lcd_send_cmd_poll(0x40 | (location << 3));
    HAL_Delay(1);
    for(int i = 0; i < 8; i++)
    {
        lcd_send_data_poll(charmap[i]);
        HAL_Delay(1);
    }
}


void lcd_enable(uint8_t en)
{
    lcd_en = en;
    if(en) { i2c_s = 0; i2c_pas = 0; }
}

void lcd_backlight(uint8_t on_off)
{
    b_Light = on_off ? BL : 0;
}

void lcd_setCur(char row, char col)
{
    cur[0] = row;
    cur[1] = col;
}

void lcd_clear(uint8_t tam2_ilk0_ikinci1)
{
    switch(tam2_ilk0_ikinci1)
    {
        case 0:  lcd_print_cur(0, 0, "                "); break;
        case 1:  lcd_print_cur(1, 0, "                "); break;
        default:
            lcd_print_cur(0, 0, "                ");
            lcd_print_cur(1, 0, "                ");
            break;
    }
}

void lcd_karakter(char harf)
{
    satir[cur[0]][cur[1]] = harf;
    cur[1]++;
}

void lcd_print_cur(uint8_t satir0_1, uint8_t sutun0_15, char* metin)
{
    char *su = satir[satir0_1] + sutun0_15;
    cur[0] = satir0_1;
    cur[1] = sutun0_15;
    while(*metin && *su)
    {
        switch(*metin)
        {
            case 0b11000011:
                metin++;
                if((*metin==188)||(*metin==156))     { *su++=0b11110101; metin++; cur[1]++; }
                else if(*metin==0b10110110)           { *su++=0b11101111; metin++; cur[1]++; }
                else if(*metin==0b10010110)           { *su++=0b11101111; metin++; cur[1]++; }
                else if((*metin==167)||(*metin==135)) { *su++=(1);        metin++; cur[1]++; }
                break;
            case 0b11000100:
                metin++;
                if     (*metin==0b10110001) { *su++=(2); metin++; cur[1]++; }
                else if(*metin==0b10110000) { *su++=(3); metin++; cur[1]++; }
                break;
            case 0b10011111: *su++=(4); metin++; cur[1]++; break;
            case 0b10011110: *su++=(5); metin++; cur[1]++; break;
            case 0b11000101: *su++=(0); metin++; metin++; cur[1]++; break;
            default: *su++=*metin++; cur[1]++; break;
        }
    }
}

void lcd_print(char* metin)
{
    char *su = satir[cur[0]] + cur[1];
    while(*metin && cur[1] < 16)
    {
        switch(*metin)
        {
            case 0b11000011:
                metin++;
                if((*metin==188)||(*metin==156))     { *su++=0b11110101; metin++; cur[1]++; }
                else if(*metin==0b10110110)           { *su++=0b11101111; metin++; cur[1]++; }
                else if(*metin==0b10010110)           { *su++=0b11101111; metin++; cur[1]++; }
                else if((*metin==167)||(*metin==135)) { *su++=(1);        metin++; cur[1]++; }
                break;
            case 0b11000100:
                metin++;
                if     (*metin==0b10110001) { *su++=(2); metin++; cur[1]++; }
                else if(*metin==0b10110000) { *su++=(3); metin++; cur[1]++; }
                break;
            case 0b10011111: *su++=(4); metin++; cur[1]++; break;
            case 0b10011110: *su++=(5); metin++; cur[1]++; break;
            case 0b11000101: *su++=(0); metin++; metin++; cur[1]++; break;
            default: *su++=*metin++; cur[1]++; break;
        }
    }
}

void lcd_print_int_cur(uint8_t satir0_1, uint8_t sutun0_15, int32_t sayi, uint8_t alan)
{
    uint8_t buffer[10];
    cur[0]=satir0_1;
    cur[1]=sutun0_15;
    for(uint8_t i=sutun0_15;i<(sutun0_15+alan);i++)
    {
        satir[satir0_1][i]=' ';
    }
    char *su=satir[satir0_1]+sutun0_15;
    uint8_t i=0;
    if(sayi<0)
    {
        *su++='-';
        sayi=-sayi;
        cur[1]++;
    }

    if(sayi>99999999)
    {

        buffer[i]=sayi/100000000;
        sayi-=buffer[i]*100000000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if((sayi>9999999)||i>0)
    {
        buffer[i]=sayi/10000000;
        sayi-=buffer[i]*10000000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>999999||i>0)
    {
        buffer[i]=sayi/1000000;
        sayi-=buffer[i]*1000000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>99999||i>0)
    {
        buffer[i]=sayi/100000;
        sayi-=buffer[i]*100000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>9999||i>0)
    {
        buffer[i]=sayi/10000;
        sayi-=buffer[i]*10000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }

    if(sayi>999||i>0)
    {
        buffer[i]=sayi/1000;
        sayi-=buffer[i]*1000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>99||i>0)
    {
        buffer[i]=sayi/100;
        sayi-=buffer[i]*100;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>9||i>0)
    {
        buffer[i]=sayi/10;
        sayi-=buffer[i]*10;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }

    *su=sayi+48;
    cur[1]++;
}

void lcd_print_int(int32_t sayi, uint8_t alan)
{
    for(uint8_t i=cur[1];i<(cur[1]+alan);i++)
    {
        satir[cur[0]][i]=' ';
    }
    uint8_t buffer[10];
    char *su=satir[cur[0]]+cur[1];
    uint8_t i=0;
    if(sayi<0)
    {
        *su++='-';
        sayi=-sayi;
        cur[1]++;
    }


    if(sayi>99999999)
    {

        buffer[i]=sayi/100000000;
        sayi-=buffer[i]*100000000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if((sayi>9999999)||i>0)
    {
        buffer[i]=sayi/10000000;
        sayi-=buffer[i]*10000000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>999999||i>0)
    {
        buffer[i]=sayi/1000000;
        sayi-=buffer[i]*1000000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>99999||i>0)
    {
        buffer[i]=sayi/100000;
        sayi-=buffer[i]*100000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>9999||i>0)
    {
        buffer[i]=sayi/10000;
        sayi-=buffer[i]*10000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }

    if(sayi>999||i>0)
    {
        buffer[i]=sayi/1000;
        sayi-=buffer[i]*1000;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>99||i>0)
    {
        buffer[i]=sayi/100;
        sayi-=buffer[i]*100;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    if(sayi>9||i>0)
    {
        buffer[i]=sayi/10;
        sayi-=buffer[i]*10;
        *su++=buffer[i]+48;
        cur[1]++;
        i++;
    }
    *su=sayi+48;
    cur[1]++;
}

static inline void lcd_print_ondalik(int32_t sayi, uint8_t ondalik)
{
    if(ondalik>6)ondalik=6;
        for(uint8_t i=cur[1];i<(cur[1]+ondalik);i++)
        {
            satir[cur[0]][i]=' ';
        }
        uint8_t buffer[10];
        char *su=satir[cur[0]]+cur[1];
        uint8_t i=0;
        switch(ondalik)
        {
        case 6:
            buffer[i]=sayi/1000000;
            sayi-=buffer[i]*1000000;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        case 5:
            buffer[i]=sayi/100000;
            sayi-=buffer[i]*100000;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        case 4:
            buffer[i]=sayi/10000;
            sayi-=buffer[i]*10000;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        case 3:
            buffer[i]=sayi/1000;
            sayi-=buffer[i]*1000;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        case 2:
            buffer[i]=sayi/100;
            sayi-=buffer[i]*100;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        case 1:
            buffer[i]=sayi/10;
            sayi-=buffer[i]*10;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        case 0:
            buffer[i]=sayi;
            if(i>0)
            {
                *su++=buffer[i]+48;
                cur[1]++;
            }
            i++;
        }
}

void lcd_print_f_cur(uint8_t satir0_1, uint8_t sutun0_15, double sayi, uint8_t ondalik)
{
    if(ondalik>6) ondalik=6;
    cur[0]=satir0_1; cur[1]=sutun0_15;
    if(sayi<0){ lcd_print("-"); sayi=-sayi; }

    int32_t fixed, tam, kesir;
    switch(ondalik)
            {
                case 1:
                    fixed = (int32_t)(sayi * 10.0f + 0.5f);
                    tam = fixed / 10;
                    kesir = fixed % 10;
                    break;
                case 2:
                    fixed = (int32_t)(sayi * 100.0f + 0.5f);
                    tam = fixed / 100;
                    kesir = fixed % 100;
                    break;
                case 3:
                    fixed = (int32_t)(sayi * 1000.0f + 0.5f);
                    tam = fixed / 1000;
                    kesir = fixed % 1000;
                    break;
                case 4:
                    fixed = (int32_t)(sayi * 10000.0f + 0.5f);
                    tam = fixed / 10000;
                    kesir = fixed % 10000;
                    break;
                case 5:
                    fixed = (int32_t)(sayi * 100000.0f + 0.5f);
                    tam = fixed / 100000;
                    kesir = fixed % 100000;
                    break;
                case 6:
                    fixed = (int32_t)(sayi * 1000000.0f + 0.5f);
                    tam = fixed / 1000000;
                    kesir = fixed % 1000000;
                    break;
                default:
                    tam = (int32_t)sayi;
                    kesir = 0;
                    break;
            }
    lcd_print_int_cur(cur[0], cur[1], tam, 1);
    if(ondalik>0){ lcd_print("."); lcd_print_ondalik(kesir, ondalik); }
}

void lcd_print_f(double sayi, uint8_t ondalik)
{
    lcd_print_f_cur(cur[0], cur[1], sayi, ondalik);
}

i2c-lcd-4bit.h

C:
#ifndef I2C_LCD_4BIT_H
#define I2C_LCD_4BIT_H

#include "stm32f1xx_hal.h"
#include <stdint.h>

// PCF8574 kontrol bitleri
#define RS      0x01
#define RW      0x02
#define E       0x04
#define BL      0x08

// PCF8574 I2C adresi (A0=A1=A2=1 → 0x27 << 1)
#define ADRES_LCD   0x4E

void lcd_init        (void);
void lcd_tarama      (void);
void lcd_enable      (uint8_t en);
void lcd_backlight   (uint8_t on_off);
void lcd_setCur      (char row, char col);
void lcd_clear       (uint8_t tam2_ilk0_ikinci1);
void lcd_karakter    (char harf);
void lcd_print       (char* metin);
void lcd_print_cur   (uint8_t satir0_1, uint8_t sutun0_15, char* metin);
void lcd_print_int   (int32_t sayi, uint8_t alan);
void lcd_print_int_cur(uint8_t satir0_1, uint8_t sutun0_15, int32_t sayi, uint8_t alan);
void lcd_print_f     (double sayi, uint8_t ondalik);
void lcd_print_f_cur (uint8_t satir0_1, uint8_t sutun0_15, double sayi, uint8_t ondalik);
void createChar      (uint8_t location, uint8_t charmap[]);

#endif

test kodu:

C:
/* USER CODE BEGIN Includes */
#include "i2c-lcd-4bit.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
int tam,kesir,sn,sayac,sure1,sure2;
/* USER CODE END PV */

/* USER CODE BEGIN 2 */
  GPIOA->BSRR = GPIO_PIN_7;
  lcd_init();
  lcd_enable(1);
  HAL_TIM_Base_Start(&htim2);
  HAL_TIM_Base_Start(&htim4);

  double test_d =1234.5678;

  /* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
  while (1)
  {
          //her ms
          if(TIM4->CNT>9){
              TIM4->CNT=0;
              sayac++;
              // lcd tarama fonksiyonunun döngüde aldığı süre
              GPIOA->BSRR = (uint32_t)GPIO_PIN_7 << 16;
            TIM2->CNT = 0;
            lcd_tarama();
            GPIOA->BSRR = GPIO_PIN_7;
            sure1 = TIM2->CNT;
            // saniyede 1 çalışan döngü
            if (sayac>1000) {
                //tarama fonksiyonunun süresini yazdır
                lcd_print_cur(1, 0, "tarama: ");
                lcd_print_int(sure1, 10);

                //float yazdırma süresini yakala
                TIM2->CNT = 0;
                lcd_print_f_cur(0, 0, test_d,4);
                sure2 = TIM2->CNT;

                //float yazdırma süresini yazdır
                lcd_print_int_cur(0,11,sure2, 4);
                lcd_print("us");
                sayac=0;
                sn++;
                lcd_print_int_cur(1, 12, sn, 0);
            }
          }

    /* USER CODE END WHILE */

Test kodu bluepill için yazıldı cube ide ile yazıldı. i2c1 100KHz kullanılıyor, timer 2 prescaler 71, timer 4 prescaler 7199 olarak ayarlandı.

float testi "1234.5678" sayısı ascii çevrimi için 14 us sürüyor(double precision bu, eğer fonksiyonu float için ayarlarsam 2 us daha kısalıp 12 us sürüyor, tarama fonksiyonunun döngüden çaldığı süre 6us. bu süreleri osiloskop ile de teyid ettim:
1776185225691.png


intege testi "12345678" sayısı için:
1776185555937.png

8 karakter metin yazdırmak:
1776185693012.png


Tabii ki türkçe karakterle de çalışıyor.
 

Forum istatistikleri

Konular
9,085
Mesajlar
146,620
Üyeler
3,680
Son üye
MeftuN

Son kaynaklar

Back
Top