/* (c) Daniele Lacamera 2019 * GPL */ #include #include #include #include #include #include #include #include "system.h" #include "button.h" #include "img/chili.h" void usart1_init(void); void usart1_puts(char *s); #define FONT_HEIGHT 8 #define FONT_WIDTH 8 #define BGCOLOR (0xfdff) extern const unsigned char fb_font[256][FONT_WIDTH]; #define BUTTON_DEBOUNCE_TIME 100 /* Board pins: * KEYA = PC13 * KEYB = PB2 * LED1 = PB0 * LED2 = PB1 */ /* ADC pins: * PA3: LM35 temp * PA2: Light sensor * PA1: soil sensor */ #define TFT_FSMC_ADDRESS_SETUP 9 #define TFT_FSMC_ADDRESS_HOLD 1 #define TFT_FSMC_DATA_SETUP 7 #define TFT_FSMC_BUS_TURNAROUND 0 #define TFT_FSMC_CLKDIVISION 0 #define TFT_FSMC_DATALATENCY 0 #define TFT_FSMC_ACCESS_MODE 0 #define TFT_FSMC_BANK 0 #define TFT_FSMC_DATAWIDHT_16 0x10 #define TFT_FSMC_WRITE_ENABLE 0x1000 #define FSMC_BTCR(X) (((volatile uint32_t *)(FSMC_BASE)))[X] #define FSMC_E_BWTR(X) (((volatile uint32_t *)(FSMC_BASE + 0x104)))[X] #define TFT_REG (*((volatile uint16_t *) 0x60000000)) #define TFT_RAM (*((volatile uint16_t *) 0x60020000)) static void TFT_CMD(uint16_t reg, uint16_t val) { TFT_REG = (reg); TFT_RAM = (val); } #define tft_init_sequence() { \ TFT_CMD(0x0007, 0x0021); \ TFT_CMD(0x0000, 0x0001); \ TFT_CMD(0x0007, 0x0023); \ TFT_CMD(0x0010, 0x0000); \ TFT_CMD(0x0007, 0x0033); \ TFT_CMD(0x0011, 0x6800); \ TFT_CMD(0x0002, 0x0600); \ TFT_CMD(0x0012, 0x6CEB); \ TFT_CMD(0x0003, 0xA8A4); \ TFT_CMD(0x000C, 0x0000); \ TFT_CMD(0x000D, 0x080C); \ TFT_CMD(0x000E, 0x2B00); \ TFT_CMD(0x001E, 0x00B0); \ TFT_CMD(0x0001, 0x2B3F); \ TFT_CMD(0x0005, 0x0000); \ TFT_CMD(0x0006, 0x0000); \ TFT_CMD(0x0016, 0xEF1C); \ TFT_CMD(0x0017, 0x0103); \ TFT_CMD(0x000B, 0x0000); \ TFT_CMD(0x000F, 0x0000); \ TFT_CMD(0x0041, 0x0000); \ TFT_CMD(0x0042, 0x0000); \ TFT_CMD(0x0048, 0x0000); \ TFT_CMD(0x0049, 0x013F); \ TFT_CMD(0x004A, 0x0000); \ TFT_CMD(0x004B, 0x0000); \ TFT_CMD(0x0044, 0xEF00); \ TFT_CMD(0x0045, 0x0000); \ TFT_CMD(0x0046, 0x013F); \ TFT_CMD(0x0030, 0x0707); \ TFT_CMD(0x0031, 0x0204); \ TFT_CMD(0x0032, 0x0204); \ TFT_CMD(0x0033, 0x0502); \ TFT_CMD(0x0034, 0x0507); \ TFT_CMD(0x0035, 0x0204); \ TFT_CMD(0x0036, 0x0204); \ TFT_CMD(0x0037, 0x0502); \ TFT_CMD(0x003A, 0x0302); \ TFT_CMD(0x002F, 0x12BE); \ TFT_CMD(0x003B, 0x0302); \ TFT_CMD(0x0023, 0x0000); \ TFT_CMD(0x0024, 0x0000); \ TFT_CMD(0x0025, 0x8000); \ TFT_CMD(0x004e, 0x0000); \ TFT_CMD(0x004f, 0x0000); \ } #define TFT_MAX_X 320 #define TFT_MAX_Y 240 #define TFT_GO(x,y) { \ TFT_CMD(0x004E, x); \ TFT_CMD(0x004F, y); \ TFT_REG = 0x22; \ } void set_pixel(uint16_t x, uint16_t y, uint16_t val) { TFT_GO(y, 319 - x); TFT_REG = 0x22; TFT_RAM = val; } void text_at(uint16_t color, uint32_t x, uint32_t y, char *text) { uint32_t i, j; char fc; const uint8_t *render; int nc = 0; while(text[0]) { fc = text[0]; render = fb_font[(int)fc]; for (i = 0; i < FONT_HEIGHT; i++) { for (j = 0; j < FONT_WIDTH; j++) { int right_shift = (FONT_HEIGHT - 1) - j; if (render[i] & (1 << (7 - j))) { set_pixel(x + j + nc * FONT_WIDTH, y + i, color); } } } nc++; text++; } } void draw_ppm(const uint8_t *buf, uint32_t len, uint32_t w, uint16_t x, uint16_t y) { int i, r = 0; uint32_t h = (len >> 1) / w; if (((x + w) > TFT_MAX_X) || ((y + h) > TFT_MAX_Y)) return; for (i = 0; i < chili_ppm_len;) { int j = 0; for (j = 0; j < chili_ppm_width; j++) { set_pixel(x + j, y + r, *(uint16_t *)(chili_ppm + i)); i+=2; } r++; } } void fill_area(uint16_t val, uint16_t x, uint16_t y, uint16_t w, uint16_t h) { int i, r = 0; if (((x + w) > TFT_MAX_X) || ((y + h) > TFT_MAX_Y)) return; for (i = 0; i < w * h;) { int j = 0; for (j = 0; j < w; j++) { set_pixel(x + j, y + r, val); i+=2; } r++; } } static void tft_init(void) { int i; volatile uint16_t id; rcc_periph_clock_enable(RCC_FSMC); rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_GPIOD); rcc_periph_clock_enable(RCC_GPIOE); gpio_set_mode(GPIOD, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO0 | GPIO1 | GPIO4 | GPIO5 | GPIO7 | GPIO8 | GPIO9 | GPIO10 | GPIO11 | GPIO14 | GPIO15 ); gpio_set_mode(GPIOE, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO3 | GPIO7 | GPIO8 | GPIO9 | GPIO10 | GPIO11 | GPIO12 | GPIO13 | GPIO14 | GPIO15); /* B5 is backlight */ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO5); gpio_set(GPIOB, GPIO5); FSMC_BTCR(0) = TFT_FSMC_DATAWIDHT_16 | TFT_FSMC_WRITE_ENABLE; FSMC_BTCR(1) = TFT_FSMC_ADDRESS_SETUP | (TFT_FSMC_ADDRESS_HOLD << 4) | (TFT_FSMC_DATA_SETUP << 8) | (TFT_FSMC_BUS_TURNAROUND << 16) | (TFT_FSMC_CLKDIVISION << 20) | (TFT_FSMC_DATALATENCY << 24) | TFT_FSMC_ACCESS_MODE; FSMC_BTCR(0) |= 1; FSMC_E_BWTR(0) = 0x0FFFFFFF; TFT_REG = 0x00; id = TFT_RAM; tft_init_sequence(); /* Erase screen */ TFT_GO(0,0); for (i = 0; i < TFT_MAX_X * TFT_MAX_Y; i++) TFT_RAM = BGCOLOR; /* Chili */ draw_ppm(chili_ppm, chili_ppm_len, chili_ppm_width, 0, TFT_MAX_Y - 160); } static void adc_setup(void) { int i; rcc_periph_clock_enable(RCC_ADC1); rcc_periph_clock_enable(RCC_GPIOA); gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, GPIO3|GPIO1|GPIO2); adc_power_off(ADC1); adc_disable_scan_mode(ADC1); adc_set_single_conversion_mode(ADC1); adc_disable_external_trigger_regular(ADC1); adc_set_right_aligned(ADC1); adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_239DOT5CYC); adc_power_on(ADC1); for (i = 0; i < 80000; i++) ; adc_reset_calibration(ADC1); adc_calibration(ADC1); } static uint16_t adc_read(uint8_t channel, int samples) { uint8_t seq[2]; uint32_t sum = 0; uint32_t i,waiting; seq[0] = channel; ADC_CR2(ADC1) |= ADC_CR2_ADON; for (i = 0 ; i < samples; i++) { adc_set_regular_sequence(ADC1, 1, seq); ADC_CR2(ADC1) |= ADC_CR2_ADON; while (!adc_eoc(ADC1)) ; sum += adc_read_regular(ADC1); } return (uint16_t)(sum / samples); } static uint16_t loop_read(uint8_t channel) { return adc_read(channel, 8); } static uint16_t temp_read(void) { return loop_read(3); } static uint16_t cputemp_read(void) { return loop_read(16); } static uint16_t light_read(void) { return loop_read(2); } static uint16_t soil_read(void) { return loop_read(1); } static int print_int(int value, char *str) { int8_t i; uint8_t nr_digits = 0; char local_buffer[25]; char *buffer; int x = 1000 * 1000 * 1000; int started = 0; if (str) buffer = str; else buffer = local_buffer; if (value < 0) { buffer[0] = '-'; value = value * -1; nr_digits++; } while (1) { if (started || (x == 1) || ((value / x) > 0)) { buffer[nr_digits++] = '0' + value / x; started = 1; } if (x == 1) break; value %= x; x /= 10; } buffer[nr_digits] = '\0'; if (str == NULL) usart1_puts(buffer); return nr_digits; } void main(void) { volatile int button_channel = 0; uint32_t start_pressed = 0; uint32_t switchtime = 0; uint16_t light, temp= 0, soil = 0, cputemp; int i; char str[100]; rcc_clock_setup_in_hse_8mhz_out_72mhz(); //button_setup(); //button_start_read(); adc_setup(); usart1_init(); tft_init(); gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO0 | GPIO1); gpio_set(GPIOB, GPIO0 | GPIO1); usart1_puts("\e[2J"); // clear screen on terminal usart1_puts("\e[0;0H"); // home cursor gpio_clear(GPIOB, GPIO0); while(1) { uint32_t temp_integer = 0; uint32_t temp_fractions = 0; int half_degree = 0; char tmp_text[10]; int tlen = 0; uint16_t light_percent, soil_percent; temp = temp_read(); temp_integer = (temp * 330); temp_fractions = temp_integer % 4096; temp_integer >>= 12; if (temp_fractions > 1024 && temp_fractions < 3072) half_degree = 1; if (temp_fractions >= 3072) temp_integer++; soil = soil_read(); light = light_read(); usart1_puts("Temperature: "); print_int(temp_integer, NULL); if (half_degree) usart1_puts(".5"); /* Temperature on screen */ fill_area(BGCOLOR, 260, 40, 60, 20); text_at(0, 120, 40, "Temperature "); tlen = print_int(temp_integer, tmp_text); text_at(0, 260, 40, tmp_text); if (half_degree) text_at(0, 260 + 8 * tlen, 40, ".5"); usart1_puts(", Light: "); print_int(light, NULL); light_percent = (100 * light) >> 12; fill_area(BGCOLOR, 260, 70, 60, 20); text_at(0, 120, 70, "Light exposure "); tlen = print_int(light_percent, tmp_text); text_at(0, 260, 70, tmp_text); text_at(0, 280, 70, "/100"); usart1_puts(", Soil: "); print_int(soil, NULL); usart1_puts("\r\n"); soil_percent = (100 * soil) >> 12; fill_area(BGCOLOR, 260, 100, 60, 20); text_at(0, 120, 100, "Soil moisture "); tlen = print_int(soil_percent, tmp_text); text_at(0, 260, 100, tmp_text); text_at(0, 280, 100, "/100"); for (i = 0; i < 1440000; i++) ; gpio_toggle(GPIOB, GPIO1); if (button_press_pending) { button_press_pending = 0; } } }