diff --git a/docs/HD44780.pdf b/docs/HD44780.pdf new file mode 100644 index 0000000..5cd5f79 Binary files /dev/null and b/docs/HD44780.pdf differ diff --git a/src/drivers/lcd.c b/src/drivers/lcd.c new file mode 100644 index 0000000..0fcfffb --- /dev/null +++ b/src/drivers/lcd.c @@ -0,0 +1,127 @@ +#include "lcd.h" + +enum mode { + WRITE, + READ, + UNDEFINED +}; + +static int mode = UNDEFINED; + +//------------------------------------------------------------------------------ +// internal functions +int set_mode_read(void) { + // configure pins + if(io_configure(GPIOA, PIN_8 | PIN_12 | PIN_15, IO_MODE_INPUT | + IO_IN_FLOATING, 0)) return -1; + if(io_configure(GPIOB, PIN_3 | PIN_4 | PIN_13 | PIN_14 | PIN_15, + IO_MODE_INPUT | IO_IN_FLOATING, 0)) return -1; + + // put lcd in read mode + io_set(GPIOA, PIN_9); // after the pins are ready to recieve voltage + mode = READ; + + return 0; +} + +int set_mode_write(void) { + // put lcd in write mode + io_clear(GPIOA, PIN_9); //before the pin can send voltage + + // configure pins + if(io_configure(GPIOA, PIN_8 | PIN_12 | PIN_15, IO_MODE_OUTPUT | + IO_OUT_PUSH_PULL, 0)) return -1; + if(io_configure(GPIOB, PIN_3 | PIN_4 | PIN_13 | PIN_14 | PIN_15, + IO_MODE_OUTPUT | IO_OUT_PUSH_PULL, 0)) return -1; + mode = WRITE; + + return 0; +} + +void wait_for_ready(void) { + // configure the lcd + if(mode != READ) if(set_mode_read()) return; + + // read D7 pin + for(;;) { + io_set(GPIOA, PIN_11); + for(int i=0; i<1000; ++i); //timer_wait is overkill here + if(!io_read(GPIOB, PIN_4)) break; + io_clear(GPIOA, PIN_11); + for(int i=0; i<1000; ++i); //same + } + io_clear(GPIOA, PIN_11); +} + +void write_byte(uint8_t byte) { + // put the lcd bus in write mode + if(mode != WRITE) if(set_mode_write()) return; + + // start tranfert + io_set(GPIOA, PIN_11); + timer_wait_us(TIM1, 1, 0); + + // send the data + io_write(GPIOA, (byte >> 0) & 0x1, PIN_8); + io_write(GPIOA, (byte >> 1) & 0x1, PIN_12); + io_write(GPIOB, (byte >> 2) & 0x1, PIN_15); + io_write(GPIOA, (byte >> 3) & 0x1, PIN_15); + io_write(GPIOB, (byte >> 4) & 0x1, PIN_14); + io_write(GPIOB, (byte >> 5) & 0x1, PIN_3); + io_write(GPIOB, (byte >> 6) & 0x1, PIN_13); + io_write(GPIOB, (byte >> 7) & 0x1, PIN_4); + + // validate data + io_clear(GPIOA, PIN_11); +} + +//------------------------------------------------------------------------------ +// API functions +int lcd_init(TIM_TypeDef* tim) { + // disable JTAG, as it utilise needed pins, SWD remains usable in + // synchronous mode + RCC->APB2ENR |= 0x1; + AFIO->MAPR = (AFIO->MAPR & ~(0x8 << 24)) | 0x2 << 24; + + // configure the lcd control pins + if(io_configure(GPIOA, PIN_9 | PIN_10 | PIN_11, IO_MODE_OUTPUT | + IO_OUT_PUSH_PULL, 0)) return -1; + + // put the lcd bus in write mode + if(set_mode_write()) return -1; //no check in case the pins were used + //somewhere else + + // select instrution register + io_write(GPIOA, LCD_MODE_CMD, PIN_10); + + // begin initialisation sequence + timer_wait_ms(tim, 15, 0); + write_byte(LCD_FUNC_SET | LCD_FUNC_8BIT | LCD_FUNC_2LINE | + LCD_FUNC_5x8DOTS); + timer_wait_ms(tim, 5, 0); + write_byte(LCD_FUNC_SET | LCD_FUNC_8BIT | LCD_FUNC_2LINE | + LCD_FUNC_5x8DOTS); + timer_wait_us(tim, 150, 0); + write_byte(LCD_FUNC_SET | LCD_FUNC_8BIT | LCD_FUNC_2LINE | + LCD_FUNC_5x8DOTS); + wait_for_ready(); + write_byte(LCD_FUNC_SET | LCD_FUNC_8BIT | LCD_FUNC_2LINE | + LCD_FUNC_5x8DOTS); + wait_for_ready(); + write_byte(LCD_DISP_CTRL | LCD_CTRL_DISP_OFF | LCD_CTRL_CUR_OFF | + LCD_CTRL_BLINK_OFF); + wait_for_ready(); + write_byte(LCD_CLEAR); + wait_for_ready(); + write_byte(LCD_ENTRY | LCD_ENTRY_LEFT | LCD_ENTRY_SHIFT_DEC); + wait_for_ready(); + + // post initialisation setup + write_byte(LCD_DISP_CTRL | LCD_CTRL_DISP_ON | LCD_CTRL_CUR_ON | + LCD_CTRL_BLINK_ON); + wait_for_ready(); + write_byte(LCD_CLEAR); + wait_for_ready(); + + return 0; +} diff --git a/src/drivers/lcd.h b/src/drivers/lcd.h new file mode 100644 index 0000000..d7086be --- /dev/null +++ b/src/drivers/lcd.h @@ -0,0 +1,77 @@ +#ifndef LCD_H +#define LCD_H + +#include "../target/stm32f103xb.h" +#include "../config.h" +#include "timer.h" +#include "io.h" + +//------------------------------------------------------------------------------ +/* LCD mode selection */ +enum lcd_register { + LCD_MODE_CMD = 0, + LCD_MODE_DATA = 1 +}; + +//------------------------------------------------------------------------------ +/* LCD commands */ +enum lcd_command { + LCD_CLEAR = 0x01, + LCD_CR = 0x02, + LCD_ENTRY = 0x04, + LCD_DISP_CTRL = 0x08, + LCD_SHIFT = 0x10, + LCD_FUNC_SET = 0x20, + LCD_CGRAM_ADDR = 0x40, + LCD_DDRAM_ADDR = 0x80 +}; + +//------------------------------------------------------------------------------ +/* LCD_ENTRY command options */ +enum lcd_entry_option { + LCD_ENTRY_RIGHT = 0x00, + LCD_ENTRY_LEFT = 0x02, + LCD_ENTRY_SHIFT_INC = 0x01, + LCD_ENTRY_SHIFT_DEC = 0x00 +}; + +//------------------------------------------------------------------------------ +/* LCD_DISP_CTRL command options */ +enum lcd_ctrl_option { + LCD_CTRL_DISP_ON = 0x04, + LCD_CTRL_DISP_OFF = 0x00, + LCD_CTRL_CUR_ON = 0x02, + LCD_CTRL_CUR_OFF = 0x00, + LCD_CTRL_BLINK_ON = 0x01, + LCD_CTRL_BLINK_OFF = 0x00 +}; + +//------------------------------------------------------------------------------ +/* LCD_SHIFT command options */ +enum lcd_shift_option { + LCD_SHIFT_DISP = 0x08, + LCD_SHIFT_CUR = 0x00, + LCD_SHIFT_RIGHT = 0x04, + LCD_SHIFT_LEFT = 0x00 +}; + +//------------------------------------------------------------------------------ +/* LCD_FUNC_SET command options */ +enum lcd_func_option { + LCD_FUNC_8BIT = 0x10, + LCD_FUNC_4BIT = 0x00, + LCD_FUNC_2LINE = 0x08, + LCD_FUNC_1LINE = 0x00, + LCD_FUNC_5x10DOTS = 0x04, + LCD_FUNC_5x8DOTS = 0x00 +}; + +//------------------------------------------------------------------------------ +int lcd_init(TIM_TypeDef* tim); + +void lcd_write(uint8_t byte); + +uint8_t lcd_read(void); + +#endif + diff --git a/src/drivers/tags b/src/drivers/tags deleted file mode 100644 index f27e006..0000000 --- a/src/drivers/tags +++ /dev/null @@ -1,137 +0,0 @@ -!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ -!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ -!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ -!_TAG_PROGRAM_AUTHOR Universal Ctags Team // -!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ -!_TAG_PROGRAM_URL https://ctags.io/ /official site/ -!_TAG_PROGRAM_VERSION 0.0.0 /3fdf28bc/ -ADC_Data adc.h /^} ADC_Data;$/;" t typeref:struct:__anon0f12ca630108 -ADC_H adc.h /^#define ADC_H$/;" d -CLOCK_CONFIG_END rcc.h /^ CLOCK_CONFIG_END$/;" e enum:Clock_config -CLOCK_CONFIG_PERFORMANCE rcc.h /^ CLOCK_CONFIG_PERFORMANCE,$/;" e enum:Clock_config -CLOCK_CONFIG_POWERSAVE rcc.h /^ CLOCK_CONFIG_POWERSAVE,$/;" e enum:Clock_config -ClockConfig_t rcc.c /^struct ClockConfig_t {$/;" s file: -Clock_config rcc.h /^enum Clock_config {$/;" g -Clock_t rcc.h /^} Clock_t;$/;" t typeref:struct:_Clock_t -EXTI0_IRQHandler io.c /^void EXTI0_IRQHandler() {$/;" f typeref:typename:void -EXTI15_10_IRQHandler io.c /^void EXTI15_10_IRQHandler() {$/;" f typeref:typename:void -EXTI1_IRQHandler io.c /^void EXTI1_IRQHandler() {$/;" f typeref:typename:void -EXTI2_IRQHandler io.c /^void EXTI2_IRQHandler() {$/;" f typeref:typename:void -EXTI3_IRQHandler io.c /^void EXTI3_IRQHandler() {$/;" f typeref:typename:void -EXTI4_IRQHandler io.c /^void EXTI4_IRQHandler() {$/;" f typeref:typename:void -EXTI9_5_IRQHandler io.c /^void EXTI9_5_IRQHandler() {$/;" f typeref:typename:void -IO_CLEAR io.h /^#define IO_CLEAR /;" d -IO_IN_ANALOG io.h /^ IO_IN_ANALOG = (0b0 << 2),$/;" e enum:io_conf -IO_IN_FLOATING io.h /^ IO_IN_FLOATING = (0b1 << 2),$/;" e enum:io_conf -IO_IN_PULL_DOWN io.h /^ IO_IN_PULL_DOWN = (0b010 << 2),$/;" e enum:io_conf -IO_IN_PULL_UP io.h /^ IO_IN_PULL_UP = (0b110 << 2),$/;" e enum:io_conf -IO_MODE_INPUT io.h /^ IO_MODE_INPUT = (0x0),$/;" e enum:io_mode -IO_MODE_OUTPUT_FAST io.h /^ IO_MODE_OUTPUT_FAST = (0x4) \/\/ 50 MHz max$/;" e enum:io_mode -IO_MODE_OUTPUT_SLOW io.h /^ IO_MODE_OUTPUT_SLOW = (0x3), \/\/ 2MHz max$/;" e enum:io_mode -IO_MODE_OUTPUT io.h /^ IO_MODE_OUTPUT = (0x1), \/\/ 10Mhz max$/;" e enum:io_mode -IO_OUT_ALT_FNCT io.h /^ IO_OUT_ALT_FNCT = (0b10 << 2),$/;" e enum:io_conf -IO_OUT_OPEN_DRAIN io.h /^ IO_OUT_OPEN_DRAIN = (0b1 << 2)$/;" e enum:io_conf -IO_OUT_PUSH_PULL io.h /^ IO_OUT_PUSH_PULL = (0b0 << 2),$/;" e enum:io_conf -OnIO io.h /^typedef void (*OnIO)();$/;" t typeref:typename:void (*)() -OnTick timer.h /^typedef void (*OnTick)(void);$/;" t typeref:typename:void (*)(void) -PIN_0 io.h /^ PIN_0 = (1 << 0),$/;" e enum:io_pin -PIN_10 io.h /^ PIN_10 = (1 << 10),$/;" e enum:io_pin -PIN_11 io.h /^ PIN_11 = (1 << 11),$/;" e enum:io_pin -PIN_12 io.h /^ PIN_12 = (1 << 12),$/;" e enum:io_pin -PIN_13 io.h /^ PIN_13 = (1 << 13),$/;" e enum:io_pin -PIN_14 io.h /^ PIN_14 = (1 << 14),$/;" e enum:io_pin -PIN_15 io.h /^ PIN_15 = (1 << 15),$/;" e enum:io_pin -PIN_1 io.h /^ PIN_1 = (1 << 1),$/;" e enum:io_pin -PIN_2 io.h /^ PIN_2 = (1 << 2),$/;" e enum:io_pin -PIN_3 io.h /^ PIN_3 = (1 << 3),$/;" e enum:io_pin -PIN_4 io.h /^ PIN_4 = (1 << 4),$/;" e enum:io_pin -PIN_5 io.h /^ PIN_5 = (1 << 5),$/;" e enum:io_pin -PIN_6 io.h /^ PIN_6 = (1 << 6),$/;" e enum:io_pin -PIN_7 io.h /^ PIN_7 = (1 << 7),$/;" e enum:io_pin -PIN_8 io.h /^ PIN_8 = (1 << 8),$/;" e enum:io_pin -PIN_9 io.h /^ PIN_9 = (1 << 9),$/;" e enum:io_pin -PIN_ALL io.h /^ PIN_ALL = 0xFFFF$/;" e enum:io_pin -RCC_CFGR_ADCPRE_DIV_2 rcc.c /^#define RCC_CFGR_ADCPRE_DIV_2 /;" d file: -RCC_CFGR_ADCPRE_DIV_4 rcc.c /^#define RCC_CFGR_ADCPRE_DIV_4 /;" d file: -RCC_CFGR_ADCPRE_DIV_6 rcc.c /^#define RCC_CFGR_ADCPRE_DIV_6 /;" d file: -RCC_CFGR_ADCPRE_DIV_8 rcc.c /^#define RCC_CFGR_ADCPRE_DIV_8 /;" d file: -RCC_CFGR_HPRE_DIV_128 rcc.c /^#define RCC_CFGR_HPRE_DIV_128 /;" d file: -RCC_CFGR_HPRE_DIV_16 rcc.c /^#define RCC_CFGR_HPRE_DIV_16 /;" d file: -RCC_CFGR_HPRE_DIV_256 rcc.c /^#define RCC_CFGR_HPRE_DIV_256 /;" d file: -RCC_CFGR_HPRE_DIV_2 rcc.c /^#define RCC_CFGR_HPRE_DIV_2 /;" d file: -RCC_CFGR_HPRE_DIV_4 rcc.c /^#define RCC_CFGR_HPRE_DIV_4 /;" d file: -RCC_CFGR_HPRE_DIV_512 rcc.c /^#define RCC_CFGR_HPRE_DIV_512 /;" d file: -RCC_CFGR_HPRE_DIV_64 rcc.c /^#define RCC_CFGR_HPRE_DIV_64 /;" d file: -RCC_CFGR_HPRE_DIV_8 rcc.c /^#define RCC_CFGR_HPRE_DIV_8 /;" d file: -RCC_CFGR_HPRE_DIV_NONE rcc.c /^#define RCC_CFGR_HPRE_DIV_NONE /;" d file: -RCC_CFGR_PLLMUL rcc.c /^#define RCC_CFGR_PLLMUL(/;" d file: -RCC_CFGR_PPRE_DIV_16 rcc.c /^#define RCC_CFGR_PPRE_DIV_16 /;" d file: -RCC_CFGR_PPRE_DIV_2 rcc.c /^#define RCC_CFGR_PPRE_DIV_2 /;" d file: -RCC_CFGR_PPRE_DIV_4 rcc.c /^#define RCC_CFGR_PPRE_DIV_4 /;" d file: -RCC_CFGR_PPRE_DIV_8 rcc.c /^#define RCC_CFGR_PPRE_DIV_8 /;" d file: -RCC_CFGR_PPRE_DIV_NONE rcc.c /^#define RCC_CFGR_PPRE_DIV_NONE /;" d file: -RCC_HSE rcc.c /^ RCC_HSE,$/;" e enum:rcc_osc file: -RCC_HSI rcc.c /^ RCC_HSI,$/;" e enum:rcc_osc file: -RCC_LSE rcc.c /^ RCC_LSE$/;" e enum:rcc_osc file: -RCC_LSI rcc.c /^ RCC_LSI,$/;" e enum:rcc_osc file: -RCC_PLL rcc.c /^ RCC_PLL,$/;" e enum:rcc_osc file: -SYSCFG_EXTI_PA_MASK io.c /^#define SYSCFG_EXTI_PA_MASK /;" d file: -SYSCFG_EXTI_PB_MASK io.c /^#define SYSCFG_EXTI_PB_MASK /;" d file: -SYSCFG_EXTI_PC_MASK io.c /^#define SYSCFG_EXTI_PC_MASK /;" d file: -SYSCFG_EXTI_PD_MASK io.c /^#define SYSCFG_EXTI_PD_MASK /;" d file: -SYSCFG_EXTI_PE_MASK io.c /^#define SYSCFG_EXTI_PE_MASK /;" d file: -SYSCFG_EXTI_PH_MASK io.c /^#define SYSCFG_EXTI_PH_MASK /;" d file: -TIM1_UP_IRQHandler timer.c /^void TIM1_UP_IRQHandler(void) {$/;" f typeref:typename:void -TIM2_IRQHandler timer.c /^void TIM2_IRQHandler(void) {$/;" f typeref:typename:void -TIM3_IRQHandler timer.c /^void TIM3_IRQHandler(void) {$/;" f typeref:typename:void -TIM4_IRQHandler timer.c /^void TIM4_IRQHandler(void) {$/;" f typeref:typename:void -TIMER_H timer.h /^#define TIMER_H$/;" d -_Clock_t rcc.h /^typedef struct _Clock_t {$/;" s -_IO_H_ io.h /^#define _IO_H_$/;" d -_RCC_H_ rcc.h /^#define _RCC_H_$/;" d -__anon0f12ca630108 adc.h /^typedef struct {$/;" s -_clock_config rcc.c /^static struct ClockConfig_t _clock_config[] = {$/;" v typeref:struct:ClockConfig_t[] file: -adc_init adc.c /^int adc_init(ADC_TypeDef* adc) {$/;" f typeref:typename:int -adc_read adc.c /^int adc_read(void) {$/;" f typeref:typename:int -adcpre rcc.c /^ uint8_t adcpre;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: -ahb_freq rcc.c /^ uint32_t ahb_freq;$/;" m struct:ClockConfig_t typeref:typename:uint32_t file: -ahb_freq rcc.h /^ uint32_t ahb_freq;$/;" m struct:_Clock_t typeref:typename:uint32_t -apb1_freq rcc.c /^ uint32_t apb1_freq;$/;" m struct:ClockConfig_t typeref:typename:uint32_t file: -apb1_freq rcc.h /^ uint32_t apb1_freq;$/;" m struct:_Clock_t typeref:typename:uint32_t -apb1_timer_freq rcc.h /^ uint32_t apb1_timer_freq;$/;" m struct:_Clock_t typeref:typename:uint32_t -apb2_freq rcc.c /^ uint32_t apb2_freq;$/;" m struct:ClockConfig_t typeref:typename:uint32_t file: -apb2_freq rcc.h /^ uint32_t apb2_freq;$/;" m struct:_Clock_t typeref:typename:uint32_t -apb2_timer_freq rcc.h /^ uint32_t apb2_timer_freq;$/;" m struct:_Clock_t typeref:typename:uint32_t -callback1 timer.c /^static OnTick callback1 = 0;$/;" v typeref:typename:OnTick file: -callback2 timer.c /^static OnTick callback2 = 0;$/;" v typeref:typename:OnTick file: -callback3 timer.c /^static OnTick callback3 = 0;$/;" v typeref:typename:OnTick file: -callback4 timer.c /^static OnTick callback4 = 0;$/;" v typeref:typename:OnTick file: -flash_cfg rcc.c /^ uint32_t flash_cfg;$/;" m struct:ClockConfig_t typeref:typename:uint32_t file: -hpre rcc.c /^ uint8_t hpre;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: -io_cb io.c /^static OnIO io_cb[16]={$/;" v typeref:typename:OnIO[16] file: -io_clear io.c /^void io_clear(GPIO_TypeDef *gpio, uint16_t mask)$/;" f typeref:typename:void -io_conf io.h /^enum io_conf {$/;" g -io_configure io.c /^int io_configure(GPIO_TypeDef *gpio, uint16_t pin, uint8_t pin_cfg, OnIO cb) {$/;" f typeref:typename:int -io_mode io.h /^enum io_mode {$/;" g -io_pin io.h /^enum io_pin {$/;" g -io_read io.c /^uint32_t io_read(GPIO_TypeDef *gpio, uint16_t mask)$/;" f typeref:typename:uint32_t -io_set io.c /^void io_set(GPIO_TypeDef *gpio, uint16_t mask)$/;" f typeref:typename:void -io_write_n io.c /^void io_write_n(GPIO_TypeDef *gpio, uint16_t val, uint16_t mask)$/;" f typeref:typename:void -io_write io.c /^void io_write(GPIO_TypeDef *gpio, uint16_t val, uint16_t mask)$/;" f typeref:typename:void -pll_src rcc.c /^ uint8_t pll_src;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: -pllmul rcc.c /^ uint8_t pllmul;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: -ppre1 rcc.c /^ uint8_t ppre1;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: -ppre2 rcc.c /^ uint8_t ppre2;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: -rcc_config_clock rcc.c /^void rcc_config_clock(uint32_t config, Clock_t *sysclks)$/;" f typeref:typename:void -rcc_osc_off rcc.c /^static void rcc_osc_off(enum rcc_osc osc)$/;" f typeref:typename:void file: -rcc_osc_on rcc.c /^static void rcc_osc_on(enum rcc_osc osc)$/;" f typeref:typename:void file: -rcc_osc rcc.c /^enum rcc_osc {$/;" g file: -rcc_set_sysclk rcc.c /^static void rcc_set_sysclk(enum rcc_osc osc)$/;" f typeref:typename:void file: -timer_config_cb timer.c /^int timer_config_cb(TIM_TypeDef* tmr, uint32_t* clk, OnTick cb) {$/;" f typeref:typename:int -timer_set_period timer.c /^int timer_set_period(TIM_TypeDef *tmr, uint16_t tick) {$/;" f typeref:typename:int -timer_start timer.c /^void timer_start(TIM_TypeDef *tmr) {$/;" f typeref:typename:void -timer_stop timer.c /^void timer_stop(TIM_TypeDef *tmr) {$/;" f typeref:typename:void -timer_tick_init timer.c /^int timer_tick_init(TIM_TypeDef *tmr, uint16_t tick_ms, OnTick cb) {$/;" f typeref:typename:int -timer_wait_ms timer.c /^int timer_wait_ms(TIM_TypeDef* tmr, uint16_t ms, OnTick cb) {$/;" f typeref:typename:int -timer_wait_us timer.c /^int timer_wait_us(TIM_TypeDef* tmr, uint16_t us, OnTick cb) {$/;" f typeref:typename:int -type rcc.c /^ uint8_t type;$/;" m struct:ClockConfig_t typeref:typename:uint8_t file: