أولاً ، تحتاج إلى توصيل الشاشة بوحدة التحكم.
نتصل وفقًا للمخطط: PB0 - PB7 - مخرجات وحدة التحكم.
عرض تعيين رقم التعريف الشخصي:
| الرقم السري | الإشارة | احالة الإشارة |
| 1 | GND | الأرض (سلك مشترك) |
| 2 | VCC | + 5 |
| 3 | VEE | . . 10-20 , . |
| 4 | RS | : 0 – ; 1 – . |
| 5 | R/W | :
0 – ; 1 – . , . |
| 6 | EN | . , «» . |
| 7 | DB0 | . . |
| 8 | DB1 | |
| 9 | DB2 | |
| 10 | DB3 | |
| 11 | DB4 | . |
| 12 | DB5 | |
| 13 | DB6 | |
| 14 | DB7 | |
| 15 | A | (+) |
| 16 | K | (-). . |
لذلك ، الشاشة متصلة. حان الوقت لتعليم الميكروكونترولر كيفية التعامل معها. قررت إنشاء مكتبتي الخاصة حتى أتمكن من استخدامها في مشاريع مختلفة. يتكون من ملفين - lcd_20x4.h و lcd_20x4.c
لنبدأ بملف الرأس.
#ifndef LCD_LCD_20X4_2004A_LCD_20X4_H_
#define LCD_LCD_20X4_2004A_LCD_20X4_H_
#include "stm32f1xx.h"
#include "delay.h"
في البداية ، قمنا بتضمين ملف مكتبة CMSIS stm32f1xx.h لأن لدي حجر STM32F103C8T6. التشغيل التالي نقوم بتضمين الملف delay.h - هذه مكتبتي للعمل مع التأخيرات بناءً على مؤقت النظام. لن أصفه هنا ، إليك رمزه:
ملف Delay.h
#ifndef DELAY_DELAY_H_
#define DELAY_DELAY_H_
#include "stm32f1xx.h"
#define F_CPU 72000000UL
#define US F_CPU/1000000
#define MS F_CPU/1000
#define SYSTICK_MAX_VALUE 16777215
#define US_MAX_VALUE SYSTICK_MAX_VALUE/(US)
#define MS_MAX_VALUE SYSTICK_MAX_VALUE/(MS)
void delay_us(uint32_t us); // 233
void delay_ms(uint32_t ms); // 233
void delay_s(uint32_t s);
#endif /* DELAY_DELAY_H_ */
ملف Delay.c
#include "delay.h"
/* */
void delay_us(uint32_t us){ // 233 016
if (us > US_MAX_VALUE || us == 0)
return;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; // 0
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk; //
SysTick->LOAD = (US * us-1); //
SysTick->VAL = 0; // SYST_CVR
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // COUNFLAG SYST_CSR
SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk; // COUNTFLAG
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //
}
void delay_ms(uint32_t ms){ // 233
if(ms > MS_MAX_VALUE || ms ==0)
return;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
SysTick->LOAD = (MS * ms);
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk;
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
void delay_s(uint32_t s){
for(int i=0; i<s*5;i++) delay_ms(200);
}
تعتمد شاشة 2004A على جهاز التحكم HITACHI HD44780. لذلك ، دعونا نلقي نظرة على ورقة البيانات الخاصة بوحدة التحكم هذه. يحتوي الجدول 6 على نظام الأوامر ، بالإضافة إلى توقيتات تنفيذ هذه الأوامر.
دعنا نعيد كتابة الأوامر اللازمة في وحدات الماكرو في ملف الرأس:
// display commands
#define CLEAR_DISPLAY 0x1
#define RETURN_HOME 0x2
#define ENTRY_MODE_SET 0x6 // mode cursor shift rihgt, display non shift
#define DISPLAY_ON 0xC // non cursor
#define DISPLAY_OFF 0x8
#define CURSOR_SHIFT_LEFT 0x10
#define CURSOR_SHIFT_RIGHT 0x14
#define DISPLAY_SHIFT_LEFT 0x18
#define DISPLAY_SHIFT_RIGHT 0x1C
#define DATA_BUS_4BIT_PAGE0 0x28
#define DATA_BUS_4BIT_PAGE1 0x2A
#define DATA_BUS_8BIT_PAGE0 0x38
#define SET_CGRAM_ADDRESS 0x40 // usage address |= SET_CGRAM_ADDRESS
#define SET_DDRAM_ADDRESS 0x80
أنت الآن بحاجة إلى تكوين دبابيس وحدة التحكم للعمل مع الشاشة. حدد موضع البتات في منفذ ODR لوحدة التحكم. انتبه إلى PIN_D4. لقد تم تسجيل بت 10 هناك بدلاً من 4. الإخراج الرابع لا يعمل على وحدة التحكم الخاصة بي. لا أعرف ما الذي يرتبط به هذا ، ولكن في سجل ODR ، يكون هذا البت دائمًا واحدًا ، حتى قبل بدء تهيئة ساعة وحدة التحكم. لا أعرف ما الذي يرتبط به هذا ، ربما ليس الحجر أصليًا.
// ODR
#define PIN_RS 0x1
#define PIN_EN 0x2
#define PIN_D7 0x80
#define PIN_D6 0x40
#define PIN_D5 0x20
#define PIN_D4 0x400
بعد ذلك ، قمنا بإعداد سجلات التحكم للمخرجات. قررت أن أفعل ذلك في شكل وحدات ماكرو للمعالج المسبق:
#define LCD_PORT GPIOB
#define LCD_ODR LCD_PORT->ODR
#define LCD_PIN_RS() LCD_PORT->CRL &= ~GPIO_CRL_CNF0; \
LCD_PORT->CRL |= GPIO_CRL_MODE0; // PB0 -, 50
#define LCD_PIN_EN() LCD_PORT->CRL &= ~GPIO_CRL_CNF1;\
LCD_PORT->CRL |= GPIO_CRL_MODE1; // PB1
#define LCD_PIN_D7() LCD_PORT->CRL &= ~GPIO_CRL_CNF7;\
LCD_PORT->CRL |= GPIO_CRL_MODE7; // PB7
#define LCD_PIN_D6() LCD_PORT->CRL &= ~GPIO_CRL_CNF6;\
LCD_PORT->CRL |= GPIO_CRL_MODE6; // PB6
#define LCD_PIN_D5() LCD_PORT->CRL &= ~GPIO_CRL_CNF5;\
LCD_PORT->CRL |= GPIO_CRL_MODE5; // PB5
#define LCD_PIN_D4() LCD_PORT->CRH &= ~GPIO_CRH_CNF10;\
LCD_PORT->CRH |= GPIO_CRH_MODE10; // PB10
#define LCD_PIN_MASK (PIN_RS | PIN_EN | PIN_D7 | PIN_D6 | PIN_D5 | PIN_D4) // 0b0000000011110011
في نهاية ملف الرأس ، نحدد وظائف العمل مع الشاشة:
void portInit(void); //
void sendByte(char byte, int isData);
void lcdInit(void); //
void sendStr(char *str, int row ); //
#endif /* LCD_LCD_20X4_2004A_LCD_20X4_H_ */
انتهينا من ملف الرأس. لنكتب الآن تنفيذ الوظائف في ملف lcd_20x4.c.
الخطوة الأولى هي تكوين الدبابيس لتعمل مع الشاشة. يتم ذلك عن طريق وظيفة portInit (void) الباطلة:
void portInit(void){
//---------------------- ----------------------------------------------------
if(LCD_PORT == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
else if (LCD_PORT == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
else return;
//--------------------- LCD-----------------------------------------------------
LCD_PIN_RS();//
LCD_PIN_EN();
LCD_PIN_D7();
LCD_PIN_D6();
LCD_PIN_D5();
LCD_PIN_D4();
lcdInit(); //
return ;
}
بالنسبة لوظيفة lcdInit () ، فهذه هي وظيفة تهيئة العرض. دعنا نكتبها أيضًا. يعتمد على مخطط انسيابي لتهيئة عرض من ورقة بيانات:
//--------------------- -----------------------------------------------------------
void lcdInit(void){
delay_ms(15); //
sendByte(0x33, 0); // 0011
delay_us(100);
sendByte(0x32, 0); // 00110010
delay_us(40);
sendByte(DATA_BUS_4BIT_PAGE0, 0); // 4
delay_us(40);
sendByte(DISPLAY_OFF, 0); //
delay_us(40);
sendByte(CLEAR_DISPLAY, 0); //
delay_ms(2);
sendByte(ENTRY_MODE_SET, 0); //
delay_us(40);
sendByte(DISPLAY_ON, 0);//
delay_us(40);
return ;
}
تستخدم دالة التهيئة الدالة sendByte (char بايت ، int isData) الباطلة. دعونا نكتب تنفيذه. يعتمد على مخطط توقيت من ورقة بيانات:
void sendByte(char byte, int isData){
//
LCD_ODR &= ~LCD_PIN_MASK;
if(isData == 1) LCD_ODR |= PIN_RS; // RS
else LCD_ODR &= ~(PIN_RS); // RS
LCD_ODR |= PIN_EN; // E
//
if(byte & 0x80) LCD_ODR |= PIN_D7;
if(byte & 0x40) LCD_ODR |= PIN_D6;
if(byte & 0x20) LCD_ODR |= PIN_D5;
if(byte & 0x10) LCD_ODR |= PIN_D4;
LCD_ODR &= ~PIN_EN; //
LCD_ODR &= ~(LCD_PIN_MASK & ~PIN_RS);// RS
LCD_ODR |= PIN_EN;// E
//
if(byte & 0x8) LCD_ODR |= PIN_D7;
if(byte & 0x4) LCD_ODR |= PIN_D6;
if(byte & 0x2) LCD_ODR |= PIN_D5;
if(byte & 0x1) LCD_ODR |= PIN_D4;
LCD_ODR &= ~(PIN_EN);//
delay_us(40);
return;
}
الآن نحن قادرون على إرسال بايت إلى الشاشة على ناقل 4 بت. يمكن أن يكون هذا البايت أمرًا أو رمزًا. يتم تحديده عن طريق تمرير متغير isData إلى الوظيفة. حان الوقت لتعلم كيفية نقل السلاسل.
يتكون عرض 2004A من 4 أسطر من 20 حرفًا كما هو موضح في العنوان. من أجل عدم تعقيد الوظيفة ، لن أقوم بتقليم الأسطر إلى 20 حرفًا. سنرسل سلسلة من الأحرف وسلسلة لإخراجها إلى الوظيفة.
لعرض الرمز على الشاشة ، تحتاج إلى كتابته على DDRAM. عنوان DDRAM يتوافق مع الجدول:
void sendStr(char *str, int row ){
char start_address;
switch (row) {
case 1:
start_address = 0x0; // 1
break;
case 2:
start_address = 0x40; // 2
break;
case 3:
start_address = 0x14; // 3
break;
case 4:
start_address = 0x54; // 4
break;
}
sendByte((start_address |= SET_DDRAM_ADDRESS), 0); // DDRAM
delay_ms(4);
while(*str != '\0'){//
sendByte(*str, 1);
str++;
}// while
}
هذا كل شيء ، مكتبة العرض جاهزة. حان الوقت الآن لاستخدامه. في الوظيفة الرئيسية () نكتب:
portInit();//
sendStr(" HELLO, HABR", 1);
sendStr(" powered by", 2);
sendStr(" STM32F103C8T6", 3);
sendStr("Nibiru", 4);
ونحصل على النتيجة:
في الختام ، سأقدم قائمة كاملة بالملفات:
شاشة lcd_20x4.h
#ifndef LCD_LCD_20X4_2004A_LCD_20X4_H_
#define LCD_LCD_20X4_2004A_LCD_20X4_H_
#include "stm32f1xx.h"
#include "delay.h"
// display commands
#define CLEAR_DISPLAY 0x1
#define RETURN_HOME 0x2
#define ENTRY_MODE_SET 0x6 // mode cursor shift rihgt, display non shift
#define DISPLAY_ON 0xC // non cursor
#define DISPLAY_OFF 0x8
#define CURSOR_SHIFT_LEFT 0x10
#define CURSOR_SHIFT_RIGHT 0x14
#define DISPLAY_SHIFT_LEFT 0x18
#define DISPLAY_SHIFT_RIGHT 0x1C
#define DATA_BUS_4BIT_PAGE0 0x28
#define DATA_BUS_4BIT_PAGE1 0x2A
#define DATA_BUS_8BIT_PAGE0 0x38
#define SET_CGRAM_ADDRESS 0x40 // usage address |= SET_CGRAM_ADDRESS
#define SET_DDRAM_ADDRESS 0x80
// ODR
#define PIN_RS 0x1
#define PIN_EN 0x2
#define PIN_D7 0x80
#define PIN_D6 0x40
#define PIN_D5 0x20
#define PIN_D4 0x400
#define LCD_PORT GPIOB
#define LCD_ODR LCD_PORT->ODR
#define LCD_PIN_RS() LCD_PORT->CRL &= ~GPIO_CRL_CNF0; \
LCD_PORT->CRL |= GPIO_CRL_MODE0; // PB0 -, 50
#define LCD_PIN_EN() LCD_PORT->CRL &= ~GPIO_CRL_CNF1;\
LCD_PORT->CRL |= GPIO_CRL_MODE1; // PB1
#define LCD_PIN_D7() LCD_PORT->CRL &= ~GPIO_CRL_CNF7;\
LCD_PORT->CRL |= GPIO_CRL_MODE7; // PB7
#define LCD_PIN_D6() LCD_PORT->CRL &= ~GPIO_CRL_CNF6;\
LCD_PORT->CRL |= GPIO_CRL_MODE6; // PB6
#define LCD_PIN_D5() LCD_PORT->CRL &= ~GPIO_CRL_CNF5;\
LCD_PORT->CRL |= GPIO_CRL_MODE5; // PB5
#define LCD_PIN_D4() LCD_PORT->CRH &= ~GPIO_CRH_CNF10;\
LCD_PORT->CRH |= GPIO_CRH_MODE10; // PB10
#define LCD_PIN_MASK (PIN_RS | PIN_EN | PIN_D7 | PIN_D6 | PIN_D5 | PIN_D4) // 0b0000000011110011
void portInit(void); //
void sendByte(char byte, int isData);
void lcdInit(void); //
void sendStr(char *str, int row ); //
#endif /* LCD_LCD_20X4_2004A_LCD_20X4_H_ */
lcd_20x4.c
#include "lcd_20x4.h"
// LCD
void sendByte(char byte, int isData){
//
LCD_ODR &= ~LCD_PIN_MASK;
if(isData == 1) LCD_ODR |= PIN_RS; // RS
else LCD_ODR &= ~(PIN_RS); // RS
//
if(byte & 0x80) LCD_ODR |= PIN_D7;
if(byte & 0x40) LCD_ODR |= PIN_D6;
if(byte & 0x20) LCD_ODR |= PIN_D5;
if(byte & 0x10) LCD_ODR |= PIN_D4;
// E
LCD_ODR |= PIN_EN;
LCD_ODR &= ~PIN_EN; //
// RS
LCD_ODR &= ~(LCD_PIN_MASK & ~PIN_RS);
//
if(byte & 0x8) LCD_ODR |= PIN_D7;
if(byte & 0x4) LCD_ODR |= PIN_D6;
if(byte & 0x2) LCD_ODR |= PIN_D5;
if(byte & 0x1) LCD_ODR |= PIN_D4;
// E
LCD_ODR |= PIN_EN;
//delay_us(10);
//
LCD_ODR &= ~(PIN_EN);
delay_us(40);
return;
}
// 50
void portInit(void){
//---------------------- ----------------------------------------------------
if(LCD_PORT == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
else if (LCD_PORT == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
else return;
//--------------------- LCD-----------------------------------------------------
LCD_PIN_RS();
LCD_PIN_EN();
LCD_PIN_D7();
LCD_PIN_D6();
LCD_PIN_D5();
LCD_PIN_D4();
lcdInit();
return ;
}
//--------------------- -----------------------------------------------------------
void lcdInit(void){
delay_ms(15); //
sendByte(0x33, 0); // 0011
delay_us(100);
sendByte(0x32, 0); // 00110010
delay_us(40);
sendByte(DATA_BUS_4BIT_PAGE0, 0); // 4
delay_us(40);
sendByte(DISPLAY_OFF, 0); //
delay_us(40);
sendByte(CLEAR_DISPLAY, 0); //
delay_ms(2);
sendByte(ENTRY_MODE_SET, 0); //
delay_us(40);
sendByte(DISPLAY_ON, 0);//
delay_us(40);
return ;
}
void sendStr(char *str, int row ){
char start_address;
switch (row) {
case 1:
start_address = 0x0; // 1
break;
case 2:
start_address = 0x40; // 2
break;
case 3:
start_address = 0x14; // 3
break;
case 4:
start_address = 0x54; // 4
break;
}
sendByte((start_address |= SET_DDRAM_ADDRESS), 0); // DDRAM
delay_ms(4);
while(*str != '\0'){
sendByte(*str, 1);
str++;
//delay_ms(100);
}// while
}