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 :
i2c-lcd-4bit.h
test kodu:
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:
intege testi "12345678" sayısı için:
8 karakter metin yazdırmak:
Tabii ki türkçe karakterle de çalışıyor.
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:
intege testi "12345678" sayısı için:
8 karakter metin yazdırmak:
Tabii ki türkçe karakterle de çalışıyor.