/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ /* Thanks to * https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung * for the basic code construct */ #include #include #include #include #include #include #include #include static void lcd_port_clear(void) { LCD_DPORT->ODR &= ~(LCD_E_MASK); LCD_DPORT->ODR &= ~(LCD_DATA_MASK | LCD_RS_MASK | LCD_E_MASK); } static void lcd_enable(void) { LCD_DPORT->ODR |= LCD_E_MASK; __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); LCD_DPORT->ODR &= ~LCD_E_MASK; __ASM("nop"); __ASM("nop"); __ASM("nop"); } static void lcd_out(uint8_t data) { LCD_DPORT->ODR &= ~(LCD_DATA_MASK); LCD_DPORT->ODR |= (data << LCD_DATA_BIT_OFFSET) & LCD_DATA_MASK; } static void lcd_data(uint8_t data) { lcd_port_clear(); LCD_DPORT->ODR |= LCD_RS_MASK; lcd_out((data>>4) & 0xFU); lcd_enable(); lcd_out(data & 0xFU); lcd_enable(); systick_wait_ms(1); } static void lcd_command(uint8_t data) { lcd_port_clear(); lcd_out((data>>4) & 0xFU); lcd_enable(); lcd_out(data & 0xFU); lcd_enable(); systick_wait_ms(1); } #define LCD_DDADR_LINE1 0x00 #define LCD_DDADR_LINE2 0x40 #define LCD_DDADR_LINE3 0x10 #define LCD_DDADR_LINE4 0x50 // Clear Display -------------- 0b00000001 #define LCD_CLEAR_DISPLAY 0x01 // Cursor Home ---------------- 0b0000001x #define LCD_CURSOR_HOME 0x02 // Set Entry Mode ------------- 0b000001xx #define LCD_SET_ENTRY 0x04 #define LCD_ENTRY_DECREASE 0x00 #define LCD_ENTRY_INCREASE 0x02 #define LCD_ENTRY_NOSHIFT 0x00 #define LCD_ENTRY_SHIFT 0x01 // Set Display ---------------- 0b00001xxx #define LCD_SET_DISPLAY 0x08 #define LCD_DISPLAY_OFF 0x00 #define LCD_DISPLAY_ON 0x04 #define LCD_CURSOR_OFF 0x00 #define LCD_CURSOR_ON 0x02 #define LCD_BLINKING_OFF 0x00 #define LCD_BLINKING_ON 0x01 // Set Shift ------------------ 0b0001xxxx #define LCD_SET_SHIFT 0x10 #define LCD_CURSOR_MOVE 0x00 #define LCD_DISPLAY_SHIFT 0x08 #define LCD_SHIFT_LEFT 0x00 #define LCD_SHIFT_RIGHT 0x04 // Set Function --------------- 0b001xxxxx #define LCD_SET_FUNCTION 0x20 #define LCD_FUNCTION_4BIT 0x00 #define LCD_FUNCTION_8BIT 0x10 #define LCD_FUNCTION_1LINE 0x00 #define LCD_FUNCTION_2LINE 0x08 #define LCD_FUNCTION_5X7 0x00 #define LCD_FUNCTION_5X10 0x04 #define LCD_SOFT_RESET 0x30 // Set CG RAM Address --------- 0b01xxxxxx (Character Generator RAM) #define LCD_SET_CGADR 0x40 #define LCD_GC_CHAR0 0 #define LCD_GC_CHAR1 1 #define LCD_GC_CHAR2 2 #define LCD_GC_CHAR3 3 #define LCD_GC_CHAR4 4 #define LCD_GC_CHAR5 5 #define LCD_GC_CHAR6 6 #define LCD_GC_CHAR7 7 // Set DD RAM Address --------- 0b1xxxxxxx (Display Data RAM) #define LCD_SET_DDADR 0x80 static char IN_SECTION(.ccm.bss) shadow_display[4][21]; void lcd_clear(void) { lcd_command(LCD_CLEAR_DISPLAY); systick_wait_ms(3); } void lcd_home(void) { lcd_command(LCD_CURSOR_HOME); systick_wait_ms(3); } static uint8_t lcd_get_set_cursor_cmd(uint8_t x, uint8_t y) { uint8_t data; switch (y) { case 0: /* First line */ data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x; break; case 1: /* Second Line */ data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x; break; case 2: /* Third line */ data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x; break; case 3: /* Fourth line */ data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x; break; default: /* In case of wrong line, assume first line */ data = LCD_SET_DDADR + LCD_DDADR_LINE1; break; } return data; } void lcd_setcursor(uint8_t x, uint8_t y) { uint8_t data; data = lcd_get_set_cursor_cmd(x, y); lcd_command(data); } void lcd_string(const char *data) { while (*data != '\0') lcd_data((uint8_t)*data++); } static void lcd_port_init() { LCD_DPORT->MODER &= MODER_DELETE(LCD_E) & MODER_DELETE(LCD_RS) & MODER_DELETE(LCD_DATA_BIT_OFFSET) & MODER_DELETE(LCD_DATA_BIT_OFFSET + 1) & MODER_DELETE(LCD_DATA_BIT_OFFSET + 2) & MODER_DELETE(LCD_DATA_BIT_OFFSET + 3); LCD_DPORT->MODER |= OUTPUT(LCD_E) | OUTPUT(LCD_RS) | OUTPUT(LCD_DATA_BIT_OFFSET) | OUTPUT(LCD_DATA_BIT_OFFSET + 1) | OUTPUT(LCD_DATA_BIT_OFFSET + 2) | OUTPUT(LCD_DATA_BIT_OFFSET + 3); } static void lcd_clear_shadow_buff() { int i, j; for (i = 0; i < 4; i++) { for (j = 0; j < 21; j++) { shadow_display[i][j] = 0x0; } } } void lcd_init(void) { int i; rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(LCD_RCC_MASK)); lcd_port_init(); lcd_port_clear(); lcd_clear_shadow_buff(); systick_wait_ms(100); LCD_DPORT->ODR |= (0x3 << LCD_DATA_BIT_OFFSET); for (i = 0; i < 3; i++) { lcd_enable(); systick_wait_ms(5); } // Set 4 Bit mode lcd_port_clear(); LCD_DPORT->ODR |= (0x2<AHB1ENR, BITMASK_TO_BITNO(LCD_RCC_MASK)); } static uint8_t compare_input_to_shadow(const char (*display_buffer)[21]) { uint8_t ret = 0; int i, row; for (row = 0; row < 4; row++) { for (i = 0; i < 20; i++) { if (display_buffer[row][i] != shadow_display[row][i]) { ret |= (1U<ODR |= LCD_E_MASK; else LCD_DPORT->ODR &= ~LCD_E_MASK; __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); } static void lcd_fsm_write_command(bool high, uint8_t cmd) { lcd_port_clear(); if (high) lcd_out((cmd >> 4) & 0x0F); else lcd_out((cmd) & 0x0F); } static void lcd_fsm_write_data(bool high, uint8_t data) { LCD_DPORT->ODR |= (1<> 4) & 0x0F); else lcd_out((data) & 0x0F); } enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21]) { static bool IN_SECTION(.ccm.data) idle = true; static uint8_t IN_SECTION(.ccm.bss) rows_to_handle = 0; static uint32_t IN_SECTION(.ccm.bss) state_cnt; static uint8_t IN_SECTION(.ccm.bss) row_cnt = 0; static uint32_t IN_SECTION(.ccm.bss) char_cnt; static uint32_t IN_SECTION(.ccm.bss) line_len; static uint64_t IN_SECTION(.ccm.bss) timestamp = 0ULL; enum lcd_fsm_ret ret; ret = LCD_FSM_NOP; if (idle) { rows_to_handle = compare_input_to_shadow(display_buffer); memcpy(shadow_display, display_buffer, sizeof(shadow_display)); shadow_display[0][20] = 0; shadow_display[1][20] = 0; shadow_display[2][20] = 0; shadow_display[3][20] = 0; state_cnt = 0; row_cnt = 0; idle = false; } if (rows_to_handle == 0) { idle = true; return ret; } if ((rows_to_handle & (1<= line_len) ? ' ' : shadow_display[row_cnt][char_cnt]); lcd_fsm_enable(true); ret = LCD_FSM_CALL_AGAIN; state_cnt++; break; case 6: lcd_fsm_enable(false); ret = LCD_FSM_WAIT_CALL; state_cnt++; break; case 7: lcd_fsm_write_data(false, (char_cnt >= line_len) ? ' ' : shadow_display[row_cnt][char_cnt]); lcd_fsm_enable(true); ret = LCD_FSM_CALL_AGAIN; state_cnt++; break; case 8: lcd_fsm_enable(false); ret = LCD_FSM_WAIT_CALL; char_cnt++; if (char_cnt < LCD_CHAR_WIDTH) { state_cnt = 5; } else { state_cnt = 0; rows_to_handle &= (uint8_t)~(1U<