426 lines
10 KiB
C
426 lines
10 KiB
C
/* (c) Daniele Lacamera 2019
|
|
* GPL
|
|
*/
|
|
#include <unicore-mx/stm32/rcc.h>
|
|
#include <unicore-mx/stm32/gpio.h>
|
|
#include <unicore-mx/stm32/adc.h>
|
|
#include <unicore-mx/stm32/usart.h>
|
|
#include <unicore-mx/stm32/fsmc.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#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;
|
|
}
|
|
}
|
|
}
|
|
|