LCD های کاراکتری معمولا جزو یکی اولین تجربههای ما در زمان شروع کاربا میکروکنترلرهای مختلف هستند. این lcd ها ساختار ساده ای دارن که شامل چند سطر کاراکتر (معمولا یک یا دو) و یک نور پس زمینه هست. آیسی HD44780 یک درایور نمایشگرهای ماتریسی است. ابن تراشه میتواند بصورت 4 و 8 بیتی مورد استفاده قرار گیرد.
احتمالا شما که خواننده این مطلب هستید تجربه کار با این نمایشگر و HD44780 را با میکروکنترلرهای AVR و یا Arduino دارید. اما در این مطلب ما قصد داریم که با استفاده از datasheet HD44780 یک کتابخانه از ابتدا برای راه اندازه نمایشگر با قابلیتهای اختصاصی مورد نظر خودمون ایجاد کنیم.
HD44780 دارای یک رم با ظرفیت 80 *8 بیت است که گنجایش 80 کاراکتر را داره.همچنین این تراشه قابلیت این رو داره که ما فونتهای اختصاصی خودمون رو به اون اضافه کنیم رو داره ما میتونیم از این قابلیت استفاده کنیم تا کاراکترهای اختصاصی خودمون رو برای نمایش تولید کنیم. اینجا اشاره کنم که در آینده با استفاده از این قابلیت قصد داریم یسری بازی رو برای اجرا روی میکروکنترلر تولید کنیم پس حتما با ادامه این سری از اموزشها همراه ما باشید.
داخل دیتاشیت این تراشه ذکر شده که قابلیت اجرای ی لیست از دستورات داره دستوراتی مثل Display clear، cursor home، display on/off، cursor on/off، display character blink، cursor shift، display shift. به شکل زیر دقت کنید در این شکل ما دو ریجستر اصلی داریم یکی رجیستر هشت بیتی Instruction Register(IR) و دیگری رجیسیتر هشت بیتی Data Register(DR) رجیستر IR تنها توسط کنترلر قابل دسترسی هست، اینجا از کلمه میکروکنترلر استفاده نمیکنم چون سیستمهای دیجیتال دیگه ای هم میتونن ازین نمایشگراستفاده کنند. رجیستر DR هم دادههایی رو که قراره روی حافظه نوشته بشه یا دستورانی که از حافظه خونده میشه رو نگهداری میکنه.
همچنین رجیستر Busy Flag(BF) زمانی که یک باشه نشون میده که تراشه مشغول پردازش دستور قبلیه و ما نمیتونیم دستور جدیدی رو براش ارسال کنیم. زمانی که پایه R/W مقدار یک داشته باشه خروجی DB7 برابر با مقدار Busy Flag هست. خب اینجا شاید جرقه ی سوال تو ذهنتون شکل بگیره که ما معمولا چیزی رو از روی این نمایشگر نمیخونیم و flag خاصی رو چک نمیکنیم، پس نقش این Flag و دستورات خوندن چی میشه؟ که در جواب این سوال باید بگم ما در کاربرد های معمول که از میکروکنترلرها استفاده میکنیم.
سعی داریم تا جایی که امکانش هست در مصرف پایهها صرفه جویی کنیم پس سعی میکنیم با نوشتن تاخیرهایی به اندازه کافی طولانی نیاز به خوندن Flag نداشته باشیم و پایه R/W رو به زمین متصل کنیم. البته تو این اموزش ما سعی داریم که قابلیت خوندن این پایه رو به برنامه کتابخونه که مینویسیم اضافه کنیم و به سرعت اجرای بالاتری دست پیدا کنیم.
رجیستر Address Counter(AC) آدرس داده ای DDRAM را نگهداری میکنه. زمانی که R/W = 1 مقدار AC روی پایههای DB0 تا DB6 قرار میگیره.
دادههابرای این که روی این حافظه نمایش داده شوند، روی DISPLAY DATA RAM (DDRAM) زخیره قرار میگیرن.این حافظه ظرفیت دخیره 80 کاراکتر را داره، برای مثال زمانی که ما H رو روی خونه اول این حافظه مینویسیم H تو خونه اول LCD نمایش داده میشه. مشخصه که اگه ابعاد LCD کوچیک تر از تعداد خونههای RAM باشه همه خونهها نمایش داده نمیشوند. تو دیتاشیت ذکر شده که از این مکانهای حافظه میشه به عنوان حافظه جنرال استفاده کرد. حالا کی ممکنه نیازش بشه که از این حافظهها استفاده کنه؟
شکل زیر ساختار حافظه DDRAM رو نشون میده.
برای مثال در یک lcd با ابعاد 16*2 خونه 1 تا 16 حافظه به سطر اول نمایشگر اختصاص داده میشوند. و خونه 40 به بعد شروع سطر دوم میشه. ادرس 40 خونه اول سطر دوم میشه.شکل زیر تاثیر شیفت دادن را بر روی محتوای DDRAM نمایش میدهد.
خب با اطلاعاتی که تا اینجا داریم میتونیم استارت کار بزنیم. خب ابتدایی ترین نیاز ما برای راه اندازی lcd دسترسی به gpio هاست. توابع HAL_GPIO باعث کند شدن برنامه میشن. پس توابع دسترسی GPIO خودمون رو بنویسیم.
برای اینکه تابع ما سرعت خوبی داشته باشه باید مستقیما رجیسترها مقدار دهی کنیم.
https://deepbluembedded.com/stm32-gpio-registers-direct-access-fast-pin-control/
ماکرو های زیر پیاده سازی میکنیم تا به رجیستر هادسترسی داشته باشیم. دقت کنید که برای Reset کردن یک پایه کافیه تو 16 بیت پر ارزش رجیستر BSRR پایه مورد نظر بنویسیم.
1 2 3 4 5 6 7 8 9 | #define GPIO_SET_PIN(port, pin) ((port)->BSRR = (pin)) #define GPIO_CLEAR_PIN(port, pin) ((port)->BSRR = (pin << 16u)) #define GPIO_TOGGLE_PIN(port, pin) ((port)->ODR ^= (pin)) #define GPIO_READ_PIN(port, pin) ((port)->IDR & (pin)) #define GPIO_WRITE_PIN(port, pin, data) (port->BSRR = (pin << 16*!(data))) |
همچنین نیاز داریم که تاخیر پیاده سازی کنیم. میتونیم از تابع HAL_Delay استفاده کنیم، یا اینکه خودمون پیاده سازیش کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #define SYSTICK_LOAD (SystemCoreClock/1000000U) #define SYSTICK_DELAY_CALIB (SYSTICK_LOAD >> 1) #define DELAY_US(us) \ do { \ uint32_t start = SysTick->VAL; \ uint32_t ticks = (us * SYSTICK_LOAD)-SYSTICK_DELAY_CALIB; \ while((start - SysTick->VAL) < ticks); \ } while (0) #define DELAY_MS(ms) \ do { \ for (uint32_t i = 0; i < ms; ++i) { \ DELAY_US(1000); \ } \ } while ( |
خب سعی کنید با استفاده از این ماکروها یک پین 1 کنید. یا اینکه یک پین رو toggle کنید.
این آیسی هم قابلیت استفاده چهار بیتی و هم بصورت هشت بیتی را داره. این قابلیت به ما کمک میکنه که تو تعداد پایههای کنترلر صرفه جویی کنیم.تو حالت چهار بیتی از پایههای DB7 تا DB4 استفاده میشه. تو این حالت انتقال یک دستور یا بایت داده در دو مرحله انجام میشه، چهار بیت پر ارزش در ابتدا و بعد از اون چهار بیت کم ارزش انتقال پیدا میکنند. شکل زیر فرایند انتقال یک دستور به نمایشگر رو نمایش میدهد.
دقت کنید برای ارسال یک instruction هر دو بیت RS و R/W باید صفر باید. جدول زیر وضعیت پایههای RS و R/W را نمایش میدهد.
1 2 3 4 5 6 7 8 9 10 11 | void DATA_write(unsigned char data) { GPIO_WRITE_PIN(lcd_pin.D4_GPIO, lcd_pin.D4_PIN, data & 0x1); GPIO_WRITE_PIN(lcd_pin.D5_GPIO, lcd_pin.D5_PIN, data & 0x2); GPIO_WRITE_PIN(lcd_pin.D6_GPIO, lcd_pin.D6_PIN, data & 0x4); GPIO_WRITE_PIN(lcd_pin.D7_GPIO, lcd_pin.D7_PIN, data & 0x8); } |
برای ارسال یک دستور نیازه که پین EN یک بار صفر و یک بشه مثل عکس بالا، پس تابع زیر برای نوشتن دستور مینویسیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | void IR_write(unsigned char IR) { GPIO_WRITE_PIN(lcd_pin.RS_GPIO, lcd_pin.RS_PIN, GPIO_PIN_RESET); DATA_write(IR); en_clk(); } void en_clk() { GPIO_WRITE_PIN(lcd_pin.EN_GPIO, lcd_pin.EN_PIN, GPIO_PIN_RESET); DELAY_US(5); GPIO_WRITE_PIN(lcd_pin.EN_GPIO, lcd_pin.EN_PIN, GPIO_PIN_SET); DELAY_US(5); GPIO_WRITE_PIN(lcd_pin.EN_GPIO, lcd_pin.EN_PIN, GPIO_PIN_RESET); DELAY_MS(5); } |
خب حالا به جایی رسیدیم که میتونیم یکسری دستورات رو به سمت نمایشگر ارسال کنیم. طبق دیتاشیت برای راهاندازی نمایشگر باید دستورات زیر به تراشه ارسال کنیم.
با استفاده از توابعی که بالا توسعه دادیم سعی کنید که این دستورات به lcd ارسال کنید. توصیه میکنم از لاجیک آنالایزر برای اسکن کردن سیگنالهای ورودی استفاده کنید.
تو قسمت بعدی از این سری سورس کد کامل این قسمت قرار میدم، و امکان نوشتن کاراکتر و سپس string رو به کتاخونهمون اضافه کنیم.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.