diff options
30 files changed, 1906 insertions, 1311 deletions
diff --git a/boards/base/STM32F746-Discovery/board.mk b/boards/base/STM32F746-Discovery/board.mk index dd1f8441..9386fae0 100644 --- a/boards/base/STM32F746-Discovery/board.mk +++ b/boards/base/STM32F746-Discovery/board.mk @@ -18,8 +18,9 @@ ifeq ($(OPT_OS),raw32) GFXSRC += $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_startup.s \ $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c \ $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_system.c \ - $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_interrupts.c - GFXDEFS += GFX_OS_EXTRA_INIT_FUNCTION=Raw32OSInit + $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_interrupts.c \ + $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f7_i2c.c + GFXDEFS += GFX_OS_EXTRA_INIT_FUNCTION=Raw32OSInit GFX_OS_INIT_NO_WARNING=TRUE SRCFLAGS+= -std=c99 GFXINC += $(CMSIS)/Device/ST/STM32F7xx/Include \ $(CMSIS)/Include \ @@ -27,4 +28,5 @@ ifeq ($(OPT_OS),raw32) LDSCRIPT = $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746nghx_flash.ld endif -include $(GFXLIB)/drivers/gdisp/STM32LTDC/driver.mk
\ No newline at end of file +include $(GFXLIB)/drivers/gdisp/STM32LTDC/driver.mk +include $(GFXLIB)/drivers/ginput/touch/FT5336/driver.mk
\ No newline at end of file diff --git a/boards/base/STM32F746-Discovery/board_STM32LTDC.h b/boards/base/STM32F746-Discovery/board_STM32LTDC.h index 059e7e9d..ffe28e02 100644 --- a/boards/base/STM32F746-Discovery/board_STM32LTDC.h +++ b/boards/base/STM32F746-Discovery/board_STM32LTDC.h @@ -44,8 +44,6 @@ static const ltdcConfig driverCfg = { static void configureLcdPins(void) { - GPIO_InitTypeDef gpio_init_structure; - // Enable GPIOs clock RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; // GPIOE RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; // GPIOG diff --git a/boards/base/STM32F746-Discovery/example_raw32/Makefile b/boards/base/STM32F746-Discovery/example_raw32/Makefile index 298d74bf..dee0f01a 100644 --- a/boards/base/STM32F746-Discovery/example_raw32/Makefile +++ b/boards/base/STM32F746-Discovery/example_raw32/Makefile @@ -55,7 +55,7 @@ LDFLAGS = SRC = OBJS = -DEFS = GOS_RAW_HEAP_SIZE=40960 +DEFS = GFX_OS_HEAP_SIZE=40960 LIBS = INCPATH = diff --git a/boards/base/STM32F746-Discovery/gmouse_lld_FT5336_board.h b/boards/base/STM32F746-Discovery/gmouse_lld_FT5336_board.h new file mode 100644 index 00000000..8031eca5 --- /dev/null +++ b/boards/base/STM32F746-Discovery/gmouse_lld_FT5336_board.h @@ -0,0 +1,76 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _GINPUT_LLD_MOUSE_BOARD_H +#define _GINPUT_LLD_MOUSE_BOARD_H + +#include "stm32f7xx.h" +#include "stm32f7_i2c.h" + +// Resolution and Accuracy Settings +#define GMOUSE_FT5336_PEN_CALIBRATE_ERROR 8 +#define GMOUSE_FT5336_PEN_CLICK_ERROR 6 +#define GMOUSE_FT5336_PEN_MOVE_ERROR 4 +#define GMOUSE_FT5336_FINGER_CALIBRATE_ERROR 14 +#define GMOUSE_FT5336_FINGER_CLICK_ERROR 18 +#define GMOUSE_FT5336_FINGER_MOVE_ERROR 14 + +// How much extra data to allocate at the end of the GMouse structure for the board's use +#define GMOUSE_FT5336_BOARD_DATA_SIZE 0 + +// The FT5336 I2C slave address (including the R/W bit) +#define FT5336_SLAVE_ADDR 0x70 + +static bool_t init_board(GMouse* m, unsigned instance) +{ + (void)m; + (void)instance; + + // I2C3_SCL GPIOH7, alternate, opendrain, highspeed + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; // Enable clock for + GPIOH->MODER |= GPIO_MODER_MODER7_1; // Alternate function + GPIOH->OTYPER |= GPIO_OTYPER_OT_7; // OpenDrain + GPIOH->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR7; // LowSpeed + GPIOH->AFR[0] |= (0b0100 << 4*7); // AF4 + + // I2C3_SDA GPIOH8, alternate, opendrain, highspeed + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; // Enable clock + GPIOH->MODER |= GPIO_MODER_MODER8_1; // Alternate function + GPIOH->OTYPER |= GPIO_OTYPER_OT_8; // OpenDrain + GPIOH->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR8; // LowSpeed + GPIOH->AFR[1] |= (0b0100 << 4*0); // AF4 + + // Initialize the I2C3 peripheral + if (!(i2cInit(I2C3))) { + return FALSE; + } + + return TRUE; +} + +static void write_reg(GMouse* m, uint8_t reg, uint8_t val) +{ + (void)m; + + i2cWriteReg(I2C3, FT5336_SLAVE_ADDR, reg, val); +} + +static uint8_t read_byte(GMouse* m, uint8_t reg) +{ + (void)m; + + return i2cReadByte(I2C3, FT5336_SLAVE_ADDR, reg); +} + +static uint16_t read_word(GMouse* m, uint8_t reg) +{ + (void)m; + + return i2cReadWord(I2C3, FT5336_SLAVE_ADDR, reg); +} + +#endif /* _GINPUT_LLD_MOUSE_BOARD_H */ diff --git a/boards/base/STM32F746-Discovery/stm32f746g_raw32_system.c b/boards/base/STM32F746-Discovery/stm32f746g_raw32_system.c index 9ffe25ee..f7c4952d 100644 --- a/boards/base/STM32F746-Discovery/stm32f746g_raw32_system.c +++ b/boards/base/STM32F746-Discovery/stm32f746g_raw32_system.c @@ -176,7 +176,9 @@ void SystemInit(void) RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset PLLCFGR register */ - RCC->PLLCFGR = 0x24003010; + //RCC->PLLCFGR = 0x24003010; // From discovery example + // M = 12 = 0b1100, N = 192 = 0b11000000, P = 2 = 0b10, Q = 2 = 0b10 + RCC->PLLCFGR = 0x00C0980C; /* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF; diff --git a/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c b/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c index 3d493e5c..794d3c66 100644 --- a/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c +++ b/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c @@ -13,7 +13,6 @@ systemticks_t gfxMillisecondsToTicks(delaytime_t ms) static void SystemClock_Config(void); static void CPU_CACHE_Enable(void); -static void LCD_Config(void); void Raw32OSInit(void) { RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; @@ -65,6 +64,7 @@ void Raw32OSInit(void) { */ void SystemClock_Config(void) { +#if 0 RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; HAL_StatusTypeDef ret = HAL_OK; @@ -74,10 +74,10 @@ void SystemClock_Config(void) RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = 25; - RCC_OscInitStruct.PLL.PLLN = 400; // 432 + RCC_OscInitStruct.PLL.PLLM = 12; + RCC_OscInitStruct.PLL.PLLN = 192; // 432 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = 8; // 9 + RCC_OscInitStruct.PLL.PLLQ = 2; // 9 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); if(ret != HAL_OK) @@ -104,6 +104,46 @@ void SystemClock_Config(void) { while(1) { ; } } +#else + + RCC_OscInitTypeDef RCC_OscInitStruct; + RCC_ClkInitTypeDef RCC_ClkInitStruct; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + + __PWR_CLK_ENABLE(); + + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.HSICalibrationValue = 16; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = 12; + RCC_OscInitStruct.PLL.PLLN = 192; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; + RCC_OscInitStruct.PLL.PLLQ = 2; + HAL_RCC_OscConfig(&RCC_OscInitStruct); + + HAL_PWREx_ActivateOverDrive(); + + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; + HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_6); + + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C1; + PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1; + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); + + HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); + + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); +#endif } /** diff --git a/boards/base/STM32F746-Discovery/stm32f7_i2c.c b/boards/base/STM32F746-Discovery/stm32f7_i2c.c new file mode 100644 index 00000000..b0fa8163 --- /dev/null +++ b/boards/base/STM32F746-Discovery/stm32f7_i2c.c @@ -0,0 +1,164 @@ +#include "stm32f7_i2c.h" + +/* + * The CR2 register needs atomic access. Hence always use this function to setup a transfer configuration. + */ +static void _i2cConfigTransfer(I2C_TypeDef* i2c, uint16_t slaveAddr, uint8_t numBytes, uint32_t mode, uint32_t request) +{ + uint32_t tmpreg = 0; + + // Get the current CR2 register value + tmpreg = i2c->CR2; + + // Clear tmpreg specific bits + tmpreg &= (uint32_t) ~((uint32_t) (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP)); + + // update tmpreg + tmpreg |= (uint32_t) (((uint32_t) slaveAddr & I2C_CR2_SADD) | (((uint32_t) numBytes << 16) & I2C_CR2_NBYTES) | (uint32_t) mode | (uint32_t) request); + + // Update the actual CR2 contents + i2c->CR2 = tmpreg; +} + +/* + * According to the STM32Cube HAL the CR2 register needs to be reset after each transaction. + */ +static void _i2cResetCr2(I2C_TypeDef* i2c) +{ + i2c->CR2 &= (uint32_t) ~((uint32_t) (I2C_CR2_SADD | I2C_CR2_HEAD10R | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_RD_WRN)); +} + +bool_t i2cInit(I2C_TypeDef* i2c) +{ + // Enable I2Cx peripheral clock. + // Select APB1 as clock source + if (i2c == I2C1) { + RCC->DCKCFGR2 &= ~RCC_DCKCFGR2_I2C1SEL; + RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; + } else if (i2c == I2C2) { + RCC->DCKCFGR2 &= ~RCC_DCKCFGR2_I2C2SEL; + RCC->APB1ENR |= RCC_APB1ENR_I2C2EN; + } else if (i2c == I2C3) { + RCC->DCKCFGR2 &= ~RCC_DCKCFGR2_I2C3SEL; + RCC->APB1ENR |= RCC_APB1ENR_I2C3EN; + } else if (i2c == I2C4) { + RCC->DCKCFGR2 &= ~RCC_DCKCFGR2_I2C4SEL; + RCC->APB1ENR |= RCC_APB1ENR_I2C4EN; + } else { + return FALSE; + } + + // Disable the I2Cx peripheral + i2c->CR1 &= ~I2C_CR1_PE; + while (i2c->CR1 & I2C_CR1_PE); + + // Set timings. Asuming I2CCLK is 50 MHz (APB1 clock source) + i2c->TIMINGR = 0x40912732; // Discovery BSP code from ST examples + + // Use 7-bit addresses + i2c->CR2 &=~ I2C_CR2_ADD10; + + // Enable auto-end mode + i2c->CR2 |= I2C_CR2_AUTOEND; + + // Disable the analog filter + i2c->CR1 |= I2C_CR1_ANFOFF; + + // Disable NOSTRETCH + i2c->CR1 |= I2C_CR1_NOSTRETCH; + + // Enable the I2Cx peripheral + i2c->CR1 |= I2C_CR1_PE; + + return TRUE; +} + +void i2cSend(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t* data, uint16_t length) +{ + // We are currently not able to send more than 255 bytes at once + if (length > 255) { + return; + } + + // Setup the configuration + _i2cConfigTransfer(i2c, slaveAddr, length, (!I2C_CR2_RD_WRN) | I2C_CR2_AUTOEND, I2C_CR2_START); + + // Transmit the whole buffer + while (length > 0) { + while (!(i2c->ISR & I2C_ISR_TXIS)); + i2c->TXDR = *data++; + length--; + } + + // Wait until the transfer is complete + while (!(i2c->ISR & I2C_ISR_TXE)); + + // Wait until the stop condition was automagically sent + while (!(i2c->ISR & I2C_ISR_STOPF)); + + // Reset the STOP bit + i2c->ISR &= ~I2C_ISR_STOPF; + + // Reset the CR2 register + _i2cResetCr2(i2c); +} + +void i2cSendByte(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t data) +{ + i2cSend(i2c, slaveAddr, &data, 1); +} + +void i2cWriteReg(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t regAddr, uint8_t value) +{ + uint8_t txbuf[2]; + txbuf[0] = regAddr; + txbuf[1] = value; + + i2cSend(i2c, slaveAddr, txbuf, 2); +} + +void i2cRead(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t* data, uint16_t length) +{ + // We are currently not able to read more than 255 bytes at once + if (length > 255) { + return; + } + + // Setup the configuration + _i2cConfigTransfer(i2c, slaveAddr, length, I2C_CR2_RD_WRN | I2C_CR2_AUTOEND, I2C_CR2_START); + + // Transmit the whole buffer + for (int i = 0; i < length; i++) { + while (!(i2c->ISR & I2C_ISR_RXNE)); + data[i] = i2c->RXDR; + } + + // Wait until the stop condition was automagically sent + while (!(i2c->ISR & I2C_ISR_STOPF)); + + // Reset the STOP bit + i2c->ISR &= ~I2C_ISR_STOPF; + + // Reset the CR2 register + _i2cResetCr2(i2c); +} + +uint8_t i2cReadByte(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t regAddr) +{ + uint8_t ret = 0xAA; + + i2cSend(i2c, slaveAddr, ®Addr, 1); + i2cRead(i2c, slaveAddr, &ret, 1); + + return ret; +} + +uint16_t i2cReadWord(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t regAddr) +{ + uint8_t ret[2] = { 0xAA, 0xAA }; + + i2cSend(i2c, slaveAddr, ®Addr, 1); + i2cRead(i2c, slaveAddr, ret, 2); + + return (uint16_t)((ret[0] << 8) | (ret[1] & 0x00FF)); +} diff --git a/boards/base/STM32F746-Discovery/stm32f7_i2c.h b/boards/base/STM32F746-Discovery/stm32f7_i2c.h new file mode 100644 index 00000000..625aeed8 --- /dev/null +++ b/boards/base/STM32F746-Discovery/stm32f7_i2c.h @@ -0,0 +1,14 @@ +#pragma once + +#include "stm32f7xx.h" +#include "gfx.h" + +bool_t i2cInit(I2C_TypeDef* i2c); + +void i2cSend(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t* data, uint16_t length); +void i2cSendByte(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t data); +void i2cWriteReg(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t regAddr, uint8_t value); + +void i2cRead(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t* data, uint16_t length); +uint8_t i2cReadByte(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t regAddr); +uint16_t i2cReadWord(I2C_TypeDef* i2c, uint8_t slaveAddr, uint8_t regAddr); diff --git a/boards/base/Win32/board.mk b/boards/base/Win32/board.mk index adcbaeaf..b4577927 100644 --- a/boards/base/Win32/board.mk +++ b/boards/base/Win32/board.mk @@ -3,3 +3,7 @@ GFXSRC += GFXLIBS += include $(GFXLIB)/drivers/multiple/Win32/driver.mk include $(GFXLIB)/drivers/gaudio/Win32/driver.mk + +ifeq ($(OPT_OS),win32.raw32) + GFXDEFS += GFX_OS_INIT_NO_WARNING=TRUE +endif diff --git a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c index ba656eb0..9c2de092 100644 --- a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c +++ b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c @@ -34,7 +34,7 @@ typedef struct ltdcLayerConfig { // Frame - LLDCOLOR_TYPE *frame; // Frame buffer address + LLDCOLOR_TYPE* frame; // Frame buffer address coord_t width, height; // Frame size in pixels coord_t pitch; // Line pitch, in bytes uint16_t fmt; // Pixel format in LTDC format @@ -102,7 +102,7 @@ typedef struct ltdcConfig { /* Driver exported functions. */ /*===========================================================================*/ -static void LTDC_Reload(void) +static void _ltdc_reload(void) { LTDC->SRCR |= LTDC_SRCR_IMR; @@ -111,7 +111,7 @@ static void LTDC_Reload(void) } } -static void LTDC_LayerInit(LTDC_Layer_TypeDef* pLayReg, const ltdcLayerConfig* pCfg) +static void _ltdc_layer_init(LTDC_Layer_TypeDef* pLayReg, const ltdcLayerConfig* pCfg) { static const uint8_t fmt2Bpp[] = { 4, /* LTDC_FMT_ARGB8888 */ @@ -144,15 +144,14 @@ static void LTDC_LayerInit(LTDC_Layer_TypeDef* pLayReg, const ltdcLayerConfig* p pLayReg->CKCR = (pLayReg->CKCR & ~0x00FFFFFF) | (pCfg->keycolor & 0x00FFFFFF); pLayReg->CACR = (pLayReg->CACR & ~LTDC_LxCACR_CONSTA) | ((uint32_t)pCfg->alpha & LTDC_LxCACR_CONSTA); pLayReg->BFCR = (pLayReg->BFCR & ~(LTDC_LxBFCR_BF1 | LTDC_LxBFCR_BF2)) | ((uint32_t)pCfg->blending & (LTDC_LxBFCR_BF1 | LTDC_LxBFCR_BF2)); - for (start = 0; start < pCfg->palettelen; start++) { + for (start = 0; start < pCfg->palettelen; start++) pLayReg->CLUTWR = ((uint32_t)start << 24) | (pCfg->palette[start] & 0x00FFFFFF); - } // Final flags pLayReg->CR = (pLayReg->CR & ~LTDC_LEF_MASK) | ((uint32_t)pCfg->layerflags & LTDC_LEF_MASK); } -static void LTDC_Init(void) +static void _ltdc_init(void) { // Set up the display scanning uint32_t hacc, vacc; @@ -176,7 +175,7 @@ static void LTDC_Init(void) // Turn off the controller and its interrupts LTDC->GCR = 0; LTDC->IER = 0; - LTDC_Reload(); + _ltdc_reload(); // Set synchronization params hacc = driverCfg.hsync - 1; @@ -205,21 +204,19 @@ static void LTDC_Init(void) LTDC->BCCR = (LTDC->BCCR & ~0x00FFFFFF) | (driverCfg.bgcolor & 0x00FFFFFF); // Load the background layer - LTDC_LayerInit(LTDC_Layer1, &driverCfg.bglayer); + _ltdc_layer_init(LTDC_Layer1, &driverCfg.bglayer); // Load the foreground layer - LTDC_LayerInit(LTDC_Layer2, &driverCfg.fglayer); + _ltdc_layer_init(LTDC_Layer2, &driverCfg.fglayer); // Interrupt handling - //nvicEnableVector(STM32_LTDC_EV_NUMBER, CORTEX_PRIORITY_MASK(STM32_LTDC_EV_IRQ_PRIORITY)); - //nvicEnableVector(STM32_LTDC_ER_NUMBER, CORTEX_PRIORITY_MASK(STM32_LTDC_ER_IRQ_PRIORITY)); // Possible flags - LTDC_IER_RRIE, LTDC_IER_LIE, LTDC_IER_FUIE, LTDC_IER_TERRIE etc LTDC->IER = 0; // Set everything going - LTDC_Reload(); + _ltdc_reload(); LTDC->GCR |= LTDC_GCR_LTDCEN; - LTDC_Reload(); + _ltdc_reload(); } LLDSPEC bool_t gdisp_lld_init(GDisplay* g) @@ -232,7 +229,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay* g) init_board(g); // Initialise the LTDC controller - LTDC_Init(); + _ltdc_init(); // Initialise DMA2D #if LTDC_USE_DMA2D @@ -318,21 +315,25 @@ LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay* g) { switch(g->p.x) { case GDISP_CONTROL_POWER: - if (g->g.Powermode == (powermode_t)g->p.ptr) + // Don't do anything if it is the same power mode + if (g->g.Powermode == (powermode_t)g->p.ptr) { return; + } + switch((powermode_t)g->p.ptr) { - case powerOff: case powerOn: case powerSleep: case powerDeepSleep: - // TODO - break; default: return; } + g->g.Powermode = (powermode_t)g->p.ptr; return; case GDISP_CONTROL_ORIENTATION: - if (g->g.Orientation == (orientation_t)g->p.ptr) + // Don't do anything if it is the same power mode + if (g->g.Orientation == (orientation_t)g->p.ptr) { return; + } + switch((orientation_t)g->p.ptr) { case GDISP_ROTATE_0: case GDISP_ROTATE_180: @@ -357,6 +358,7 @@ LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay* g) default: return; } + g->g.Orientation = (orientation_t)g->p.ptr; return; @@ -398,32 +400,60 @@ LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay* g) // Uses p.x,p.y p.cx,p.cy p.color LLDSPEC void gdisp_lld_fill_area(GDisplay* g) - { - LLDCOLOR_TYPE c; - + { + uint32_t reg_omar = 0; + uint32_t reg_oor = 0; + uint32_t reg_nlr = 0; + // Wait until DMA2D is ready while (1) { if (!(DMA2D->CR & DMA2D_CR_START)) { break; } } - - c = gdispColor2Native(g->p.color); - - // Output color register - DMA2D->OCOLR = (uint32_t)c; - + + // Calculate pixel positions and stuff like that + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + default: + reg_omar = g->p.y * g->g.Width * LTDC_PIXELBYTES + g->p.x * LTDC_PIXELBYTES + (uint32_t)driverCfg.bglayer.frame; + reg_oor = g->g.Width - g->p.cx; + reg_nlr = (g->p.cx << 16) | (g->p.cy); + break; + + case GDISP_ROTATE_90: + break; + + case GDISP_ROTATE_180: + reg_omar = g->g.Width * (g->g.Height - g->p.y - g->p.cy) * LTDC_PIXELBYTES + (g->g.Width - g->p.x - g->p.cx) * LTDC_PIXELBYTES + (uint32_t)driverCfg.bglayer.frame; + reg_oor = g->g.Width - g->p.cx; + reg_nlr = (g->p.cy << 16) | (g->p.cx); + break; + + case GDISP_ROTATE_270: + break; + } + #else + reg_omar = g->p.y * g->g.Width * LTDC_PIXELBYTES + g->p.x * LTDC_PIXELBYTES + (uint32_t)driverCfg.bglayer.frame; + reg_oor = g->g.Width - g->p.cx; + reg_nlr = (g->p.cx << 16) | (g->p.cy); + #endif + // Output memory address register - DMA2D->OMAR = g->p.y * g->g.Width * LTDC_PIXELBYTES + g->p.x * LTDC_PIXELBYTES + (uint32_t)driverCfg.bglayer.frame; + DMA2D->OMAR = reg_omar; // Output offset register (in pixels) - DMA2D->OOR = g->g.Width - g->p.cx; - + DMA2D->OOR = reg_oor; + // PL (pixel per lines to be transferred); NL (number of lines) - DMA2D->NLR = (g->p.cx << 16) | (g->p.cy); + DMA2D->NLR = reg_nlr; + + // Output color register + DMA2D->OCOLR = (uint32_t)(gdispColor2Native(g->p.color)); // Set MODE to R2M and Start the process - DMA2D->CR = DMA2D_CR_MODE_R2M | DMA2D_CR_START; + DMA2D->CR = DMA2D_CR_MODE_R2M | DMA2D_CR_START; } // Uses p.x,p.y p.cx,p.cy p.x1,p.y1 (=srcx,srcy) p.x2 (=srccx), p.ptr (=buffer) diff --git a/drivers/ginput/touch/FT5336/driver.mk b/drivers/ginput/touch/FT5336/driver.mk new file mode 100644 index 00000000..be3cab6e --- /dev/null +++ b/drivers/ginput/touch/FT5336/driver.mk @@ -0,0 +1,2 @@ +# List the required driver. +GFXSRC += $(GFXLIB)/drivers/ginput/touch/FT5336/gmouse_lld_FT5336.c diff --git a/drivers/ginput/touch/FT5336/ft5336.h b/drivers/ginput/touch/FT5336/ft5336.h new file mode 100644 index 00000000..4940cc42 --- /dev/null +++ b/drivers/ginput/touch/FT5336/ft5336.h @@ -0,0 +1,241 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _FT5336_H +#define _FT5336_H + +// I2C Slave address of touchscreen FocalTech FT5336 +#define FT5336_I2C_SLAVE_ADDRESS 0x70 + +// Maximum border values of the touchscreen pad +#define FT5336_MAX_WIDTH 480 // Touchscreen pad max width +#define FT5336_MAX_HEIGHT 272 // Touchscreen pad max height + +// Possible values of driver functions return status +#define FT5336_STATUS_OK 0x00 +#define FT5336_STATUS_NOT_OK 0x01 + +// Possible values of global variable 'TS_I2C_Initialized' +#define FT5336_I2C_NOT_INITIALIZED 0x00 +#define FT5336_I2C_INITIALIZED 0x01 + +// Max detectable simultaneous touches +#define FT5336_MAX_DETECTABLE_TOUCH 0x05 + +// Current mode register of the FT5336 (R)/W +#define FT5336_DEV_MODE_REG 0x00 + +// Possible values of FT5336_DEV_MODE_REG +#define FT5336_DEV_MODE_WORKING 0x00 +#define FT5336_DEV_MODE_FACTORY 0x04 + +#define FT5336_DEV_MODE_MASK 0x07 +#define FT5336_DEV_MODE_SHIFT 0x04 + +// Gesture ID register +#define FT5336_GEST_ID_REG 0x01 + +// Possible values of FT5336_GEST_ID_REG +#define FT5336_GEST_ID_NO_GESTURE 0x00 +#define FT5336_GEST_ID_MOVE_UP 0x10 +#define FT5336_GEST_ID_MOVE_RIGHT 0x14 +#define FT5336_GEST_ID_MOVE_DOWN 0x18 +#define FT5336_GEST_ID_MOVE_LEFT 0x1C +#define FT5336_GEST_ID_SINGLE_CLICK 0x20 +#define FT5336_GEST_ID_DOUBLE_CLICK 0x22 +#define FT5336_GEST_ID_ROTATE_CLOCKWISE 0x28 +#define FT5336_GEST_ID_ROTATE_C_CLOCKWISE 0x29 +#define FT5336_GEST_ID_ZOOM_IN 0x40 +#define FT5336_GEST_ID_ZOOM_OUT 0x49 + +// Touch Data Status register : gives number of active touch points (0..5) +#define FT5336_TD_STAT_REG 0x02 + +// Values related to FT5336_TD_STAT_REG +#define FT5336_TD_STAT_MASK 0x0F +#define FT5336_TD_STAT_SHIFT 0x00 + +// Values Pn_XH and Pn_YH related +#define FT5336_TOUCH_EVT_FLAG_PRESS_DOWN 0x00 +#define FT5336_TOUCH_EVT_FLAG_LIFT_UP 0x01 +#define FT5336_TOUCH_EVT_FLAG_CONTACT 0x02 +#define FT5336_TOUCH_EVT_FLAG_NO_EVENT 0x03 + +#define FT5336_TOUCH_EVT_FLAG_SHIFT 0x06 +#define FT5336_TOUCH_EVT_FLAG_MASK (3 << FT5336_TOUCH_EVT_FLAG_SHIFT) + +#define FT5336_TOUCH_POS_MSB_MASK 0x0F +#define FT5336_TOUCH_POS_MSB_SHIFT 0x00 + +// Values Pn_XL and Pn_YL related +#define FT5336_TOUCH_POS_LSB_MASK 0xFF +#define FT5336_TOUCH_POS_LSB_SHIFT 0x00 + +// Values Pn_WEIGHT related +#define FT5336_TOUCH_WEIGHT_MASK 0xFF +#define FT5336_TOUCH_WEIGHT_SHIFT 0x00 + +// Values related to FT5336_Pn_MISC_REG +#define FT5336_TOUCH_AREA_MASK (0x04 << 4) +#define FT5336_TOUCH_AREA_SHIFT 0x04 + +#define FT5336_P1_XH_REG 0x03 +#define FT5336_P1_XL_REG 0x04 +#define FT5336_P1_YH_REG 0x05 +#define FT5336_P1_YL_REG 0x06 +#define FT5336_P1_WEIGHT_REG 0x07 +#define FT5336_P1_MISC_REG 0x08 + +#define FT5336_P2_XH_REG 0x09 +#define FT5336_P2_XL_REG 0x0A +#define FT5336_P2_YH_REG 0x0B +#define FT5336_P2_YL_REG 0x0C +#define FT5336_P2_WEIGHT_REG 0x0D +#define FT5336_P2_MISC_REG 0x0E + +#define FT5336_P3_XH_REG 0x0F +#define FT5336_P3_XL_REG 0x10 +#define FT5336_P3_YH_REG 0x11 +#define FT5336_P3_YL_REG 0x12 +#define FT5336_P3_WEIGHT_REG 0x13 +#define FT5336_P3_MISC_REG 0x14 + +#define FT5336_P4_XH_REG 0x15 +#define FT5336_P4_XL_REG 0x16 +#define FT5336_P4_YH_REG 0x17 +#define FT5336_P4_YL_REG 0x18 +#define FT5336_P4_WEIGHT_REG 0x19 +#define FT5336_P4_MISC_REG 0x1A + +#define FT5336_P5_XH_REG 0x1B +#define FT5336_P5_XL_REG 0x1C +#define FT5336_P5_YH_REG 0x1D +#define FT5336_P5_YL_REG 0x1E +#define FT5336_P5_WEIGHT_REG 0x1F +#define FT5336_P5_MISC_REG 0x20 + +#define FT5336_P6_XH_REG 0x21 +#define FT5336_P6_XL_REG 0x22 +#define FT5336_P6_YH_REG 0x23 +#define FT5336_P6_YL_REG 0x24 +#define FT5336_P6_WEIGHT_REG 0x25 +#define FT5336_P6_MISC_REG 0x26 + +#define FT5336_P7_XH_REG 0x27 +#define FT5336_P7_XL_REG 0x28 +#define FT5336_P7_YH_REG 0x29 +#define FT5336_P7_YL_REG 0x2A +#define FT5336_P7_WEIGHT_REG 0x2B +#define FT5336_P7_MISC_REG 0x2C + +#define FT5336_P8_XH_REG 0x2D +#define FT5336_P8_XL_REG 0x2E +#define FT5336_P8_YH_REG 0x2F +#define FT5336_P8_YL_REG 0x30 +#define FT5336_P8_WEIGHT_REG 0x31 +#define FT5336_P8_MISC_REG 0x32 + +#define FT5336_P9_XH_REG 0x33 +#define FT5336_P9_XL_REG 0x34 +#define FT5336_P9_YH_REG 0x35 +#define FT5336_P9_YL_REG 0x36 +#define FT5336_P9_WEIGHT_REG 0x37 +#define FT5336_P9_MISC_REG 0x38 + +#define FT5336_P10_XH_REG 0x39 +#define FT5336_P10_XL_REG 0x3A +#define FT5336_P10_YH_REG 0x3B +#define FT5336_P10_YL_REG 0x3C +#define FT5336_P10_WEIGHT_REG 0x3D +#define FT5336_P10_MISC_REG 0x3E + +// Threshold for touch detection +#define FT5336_TH_GROUP_REG 0x80 + +// Values FT5336_TH_GROUP_REG : threshold related +#define FT5336_THRESHOLD_MASK 0xFF +#define FT5336_THRESHOLD_SHIFT 0x00 + +// Filter function coefficients +#define FT5336_TH_DIFF_REG 0x85 + +// Control register +#define FT5336_CTRL_REG 0x86 + +// Values related to FT5336_CTRL_REG + +// Will keep the Active mode when there is no touching +#define FT5336_CTRL_KEEP_ACTIVE_MODE 0x00 + +// Switching from Active mode to Monitor mode automatically when there is no touching +#define FT5336_CTRL_KEEP_AUTO_SWITCH_MONITOR_MODE 0x01 + +// The time period of switching from Active mode to Monitor mode when there is no touching +#define FT5336_TIMEENTERMONITOR_REG 0x87 + +// Report rate in Active mode +#define FT5336_PERIODACTIVE_REG 0x88 + +// Report rate in Monitor mode +#define FT5336_PERIODMONITOR_REG 0x89 + +// The value of the minimum allowed angle while Rotating gesture mode +#define FT5336_RADIAN_VALUE_REG 0x91 + +// Maximum offset while Moving Left and Moving Right gesture +#define FT5336_OFFSET_LEFT_RIGHT_REG 0x92 + +// Maximum offset while Moving Up and Moving Down gesture +#define FT5336_OFFSET_UP_DOWN_REG 0x93 + +// Minimum distance while Moving Left and Moving Right gesture +#define FT5336_DISTANCE_LEFT_RIGHT_REG 0x94 + +// Minimum distance while Moving Up and Moving Down gesture +#define FT5336_DISTANCE_UP_DOWN_REG 0x95 + +// Maximum distance while Zoom In and Zoom Out gesture +#define FT5336_DISTANCE_ZOOM_REG 0x96 + +// High 8-bit of LIB Version info +#define FT5336_LIB_VER_H_REG 0xA1 + +// Low 8-bit of LIB Version info +#define FT5336_LIB_VER_L_REG 0xA2 + +// Chip Selecting +#define FT5336_CIPHER_REG 0xA3 + +// Interrupt mode register (used when in interrupt mode +#define FT5336_GMODE_REG 0xA4 + +#define FT5336_G_MODE_INTERRUPT_MASK 0x03 +#define FT5336_G_MODE_INTERRUPT_SHIFT 0x00 + +// Possible values of FT5336_GMODE_REG +#define FT5336_G_MODE_INTERRUPT_POLLING 0x00 +#define FT5336_G_MODE_INTERRUPT_TRIGGER 0x01 + +// Current power mode the FT5336 system is in (R) +#define FT5336_PWR_MODE_REG 0xA5 + +// FT5336 firmware version +#define FT5336_FIRMID_REG 0xA6 + +// FT5336 Chip identification register +#define FT5336_CHIP_ID_REG 0xA8 + +// Possible values of FT5336_CHIP_ID_REG +#define FT5336_ID_VALUE 0x51 + +// Release code version +#define FT5336_RELEASE_CODE_ID_REG 0xAF + +// Current operating mode the FT5336 system is in (R) +#define FT5336_STATE_REG 0xBC + +#endif /* _FT5336_H */ diff --git a/drivers/ginput/touch/FT5336/gmouse_lld_FT5336.c b/drivers/ginput/touch/FT5336/gmouse_lld_FT5336.c new file mode 100644 index 00000000..a55bef65 --- /dev/null +++ b/drivers/ginput/touch/FT5336/gmouse_lld_FT5336.c @@ -0,0 +1,106 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE + +#define GMOUSE_DRIVER_VMT GMOUSEVMT_FT5336 +#include "src/ginput/ginput_driver_mouse.h" + +// Get the hardware interface +#include "gmouse_lld_FT5336_board.h" + +// Hardware definitions +#include "drivers/ginput/touch/FT5336/ft5336.h" + +static bool_t ft5336Init(GMouse* m, unsigned driverinstance) +{ + // Initialize the board stuff + if (!init_board(m, driverinstance)) { + return FALSE; + } + + // We need at least 200 ms worth of delay here... + gfxSleepMilliseconds(200); + + // Check Chip ID + if (read_byte(m, FT5336_CHIP_ID_REG) != FT5336_ID_VALUE) { + return FALSE; + } + + // Disable interrupts. We use this chip in polling mode + write_reg(m, FT5336_GMODE_REG, (FT5336_G_MODE_INTERRUPT_POLLING & (FT5336_G_MODE_INTERRUPT_MASK >> FT5336_G_MODE_INTERRUPT_SHIFT)) << FT5336_G_MODE_INTERRUPT_SHIFT); + +/* + // Init default values. (From NHD-3.5-320240MF-ATXL-CTP-1 datasheet) + // Valid touching detect threshold + write_reg(m, FT5336_TH_GROUP_REG, 0x16); + + // Touch difference threshold + write_reg(m, FT5336_TH_DIFF_REG, 0xA0); + + // Delay to enter 'Monitor' status (s) + write_reg(m, FT5336_TIMEENTERMONITOR_REG, 0x0A); + + // Period of 'Active' status (ms) + write_reg(m, FT5336_PERIODACTIVE_REG, 0x06); + + // Timer to enter 'idle' when in 'Monitor' (ms) + write_reg(m, FT5336_PERIODMONITOR_REG, 0x28); +*/ + return TRUE; +} + +static bool_t ft5336ReadXYZ(GMouse* m, GMouseReading* pdr) +{ + // Assume not touched. + pdr->buttons = 0; + pdr->z = 0; + + // Only take a reading if exactly one touch contact point + if (read_byte(m, FT5336_TD_STAT_REG) == 1) { + // Get and return X, Y an Z values + pdr->y = (coord_t)(read_word(m, FT5336_P1_XH_REG) & 0x0FFF); + pdr->x = (coord_t)(read_word(m, FT5336_P1_YH_REG) & 0x0FFF); + pdr->z = 1; + } + + return TRUE; +} + +const GMouseVMT const GMOUSE_DRIVER_VMT[1] = {{ + { + GDRIVER_TYPE_TOUCH, + GMOUSE_VFLG_TOUCH | GMOUSE_VFLG_ONLY_DOWN | GMOUSE_VFLG_POORUPDOWN, + sizeof(GMouse) + GMOUSE_FT5336_BOARD_DATA_SIZE, + _gmouseInitDriver, + _gmousePostInitDriver, + _gmouseDeInitDriver + }, + 1, // z_max - (currently?) not supported + 0, // z_min - (currently?) not supported + 1, // z_touchon + 0, // z_touchoff + { // pen_jitter + GMOUSE_FT5336_PEN_CALIBRATE_ERROR, // calibrate + GMOUSE_FT5336_PEN_CLICK_ERROR, // click + GMOUSE_FT5336_PEN_MOVE_ERROR // move + }, + { // finger_jitter + GMOUSE_FT5336_FINGER_CALIBRATE_ERROR, // calibrate + GMOUSE_FT5336_FINGER_CLICK_ERROR, // click + GMOUSE_FT5336_FINGER_MOVE_ERROR // move + }, + ft5336Init, // init + 0, // deinit + ft5336ReadXYZ, // get + 0, // calsave + 0 // calload +}}; + +#endif /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */ diff --git a/drivers/ginput/touch/FT5336/gmouse_lld_FT5336_board_template.h b/drivers/ginput/touch/FT5336/gmouse_lld_FT5336_board_template.h new file mode 100644 index 00000000..1daa0b1e --- /dev/null +++ b/drivers/ginput/touch/FT5336/gmouse_lld_FT5336_board_template.h @@ -0,0 +1,59 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _GINPUT_LLD_MOUSE_BOARD_H +#define _GINPUT_LLD_MOUSE_BOARD_H + +// Resolution and Accuracy Settings +#define GMOUSE_FT5336_PEN_CALIBRATE_ERROR 8 +#define GMOUSE_FT5336_PEN_CLICK_ERROR 6 +#define GMOUSE_FT5336_PEN_MOVE_ERROR 4 +#define GMOUSE_FT5336_FINGER_CALIBRATE_ERROR 14 +#define GMOUSE_FT5336_FINGER_CLICK_ERROR 18 +#define GMOUSE_FT5336_FINGER_MOVE_ERROR 14 + +// How much extra data to allocate at the end of the GMouse structure for the board's use +#define GMOUSE_FT5336_BOARD_DATA_SIZE 0 + +// Set this to TRUE if you want self-calibration. +// NOTE: This is not as accurate as real calibration. +// It requires the orientation of the touch panel to match the display. +// It requires the active area of the touch panel to exactly match the display size. +#define GMOUSE_FT5336_SELF_CALIBRATE FALSE + +static bool_t init_board(GMouse* m, unsigned instance) +{ + (void)m; + (void)instance; + + return TRUE; +} + +static void write_reg(GMouse* m, uint8_t reg, uint8_t val) +{ + (void)m; + (void)reg; + (void)val; +} + +static uint8_t read_byte(GMouse* m, uint8_t reg) +{ + (void)m; + (void)reg; + + return (uint16_t)0x00; +} + +static uint16_t read_word(GMouse* m, uint8_t reg) +{ + (void)m; + (void)reg; + + return (uint16_t)0x0000; +} + +#endif /* _GINPUT_LLD_MOUSE_BOARD_H */ diff --git a/gfxconf.example.h b/gfxconf.example.h index 52c129ef..d46bbae2 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -35,13 +35,15 @@ //#define GFX_USE_OS_ECOS FALSE //#define GFX_USE_OS_RAWRTOS FALSE //#define GFX_USE_OS_RAW32 FALSE -// #define GOS_RAW_HEAP_SIZE 0 // #define INTERRUPTS_OFF() optional_code // #define INTERRUPTS_ON() optional_code // Options that (should where relevant) apply to all operating systems // #define GFX_COMPILER GFX_COMPILER_UNKNOWN +// #define GFX_CPU GFX_CPU_UNKNOWN +// #define GFX_OS_HEAP_SIZE 0 // #define GFX_NO_OS_INIT FALSE +// #define GFX_OS_INIT_NO_WARNING FALSE // #define GFX_OS_EXTRA_INIT_FUNCTION myOSInitRoutine // #define GFX_OS_EXTRA_DEINIT_FUNCTION myOSDeInitRoutine diff --git a/src/gos/gos.mk b/src/gos/gos.mk index 7db246d2..1c3a86a5 100644 --- a/src/gos/gos.mk +++ b/src/gos/gos.mk @@ -11,5 +11,7 @@ GFXSRC += $(GFXLIB)/src/gos/gos_chibios.c \ $(GFXLIB)/src/gos/gos_raw32.c \ $(GFXLIB)/src/gos/gos_ecos.c \ $(GFXLIB)/src/gos/gos_rawrtos.c \ - $(GFXLIB)/src/gos/gos_arduino.c + $(GFXLIB)/src/gos/gos_arduino.c \ + $(GFXLIB)/src/gos/gos_x_threads.c \ + $(GFXLIB)/src/gos/gos_x_heap.c diff --git a/src/gos/gos_arduino.c b/src/gos/gos_arduino.c index 380d528a..c565abb9 100644 --- a/src/gos/gos_arduino.c +++ b/src/gos/gos_arduino.c @@ -11,7 +11,8 @@ #include <string.h> // Prototype for memcpy() -static void _gosThreadsInit(void); +void _gosHeapInit(void); +void _gosThreadsInit(void); /********************************************************* * Initialise @@ -23,7 +24,12 @@ void _gosInit(void) * On the other hand the C runtime should still already be initialized before * getting here! */ - #warning "GOS: Arduino - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #if !GFX_OS_INIT_NO_WARNING + #warning "GOS: Arduino - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #endif + + // Start the heap allocator + _gosHeapInit(); // Start the scheduler _gosThreadsInit(); @@ -53,488 +59,16 @@ void gfxExit(void) { dummy++; } -/********************************************************* - * Head allocation functions - *********************************************************/ - -#include <stdlib.h> // Prototype for malloc(), realloc() and free() - -void *gfxAlloc(size_t sz) { - return malloc(sz); -} - -void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { - (void) oldsz; - return realloc(ptr, newsz); -} - -void gfxFree(void *ptr) { - free(ptr); -} - -/********************************************************* - * Semaphores and critical region functions - *********************************************************/ - -#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) - #define INTERRUPTS_OFF() - #define INTERRUPTS_ON() -#endif - -void gfxSystemLock(void) { - INTERRUPTS_OFF(); -} - -void gfxSystemUnlock(void) { - INTERRUPTS_ON(); -} - -void gfxMutexInit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxMutexEnter(gfxMutex *pmutex) { - INTERRUPTS_OFF(); - while (pmutex[0]) { - INTERRUPTS_ON(); - gfxYield(); - INTERRUPTS_OFF(); - } - pmutex[0] = 1; - INTERRUPTS_ON(); -} - -void gfxMutexExit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { - psem->cnt = val; - psem->limit = limit; -} - -bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { - systemticks_t starttm, delay; - - // Convert our delay to ticks - switch (ms) { - case TIME_IMMEDIATE: - delay = TIME_IMMEDIATE; - break; - case TIME_INFINITE: - delay = TIME_INFINITE; - break; - default: - delay = gfxMillisecondsToTicks(ms); - if (!delay) delay = 1; - starttm = gfxSystemTicks(); - } - - INTERRUPTS_OFF(); - while (psem->cnt <= 0) { - INTERRUPTS_ON(); - // Check if we have exceeded the defined delay - switch (delay) { - case TIME_IMMEDIATE: - return FALSE; - case TIME_INFINITE: - break; - default: - if (gfxSystemTicks() - starttm >= delay) - return FALSE; - break; - } - gfxYield(); - INTERRUPTS_OFF(); - } - psem->cnt--; - INTERRUPTS_ON(); - return TRUE; -} - -bool_t gfxSemWaitI(gfxSem *psem) { - if (psem->cnt <= 0) - return FALSE; - psem->cnt--; - return TRUE; -} - -void gfxSemSignal(gfxSem *psem) { - INTERRUPTS_OFF(); - gfxSemSignalI(psem); - INTERRUPTS_ON(); -} - -void gfxSemSignalI(gfxSem *psem) { - if (psem->cnt < psem->limit) - psem->cnt++; -} /********************************************************* * Sleep functions *********************************************************/ -void gfxSleepMilliseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); +systemticks_t gfxSystemTicks(void) { + return millis(); } - -void gfxSleepMicroseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms/1000); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); -} - -/********************************************************* - * Threading functions - *********************************************************/ - -/** - * There are some compilers we know how they store the jmpbuf. For those - * we can use the constant macro definitions. For others we have to "auto-detect". - * Auto-detection is hairy and there is no guarantee it will work on all architectures. - * For those it doesn't - read the compiler manuals and the library source code to - * work out the correct macro values. - * You can use the debugger to work out the values for your compiler and put them here. - * Defining these macros as constant values makes the system behavior guaranteed but also - * makes your code compiler and cpu architecture dependent. It also saves a heap of code - * and a few bytes of RAM. - */ -#if GFX_COMPILER == GFX_COMPILER_MINGW32 - #define AUTO_DETECT_MASK FALSE - #define STACK_DIR_UP FALSE - #define MASK1 0x00000011 - #define MASK2 0x00000000 - #define STACK_BASE 12 -#else - // Use auto-detection of the stack frame format - // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf. - #define AUTO_DETECT_MASK TRUE - #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down - #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation - #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation - #define STACK_BASE stackbase // The base of the stack frame relative to the local variables - static bool_t stackdirup; - static uint32_t jmpmask1; - static uint32_t jmpmask2; - static size_t stackbase; -#endif - -#include <setjmp.h> /* jmp_buf, setjmp(), longjmp() */ - -/** - * Some compilers define a _setjmp() and a setjmp(). - * The difference between them is that setjmp() saves the signal masks. - * That is of no use to us so prefer to use the _setjmp() methods. - * If they don't exist compile them to be the standard setjmp() function. - * Similarly for longjmp(). - */ -#if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__) - #define _setjmp setjmp -#endif -#if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__) - #define _longjmp longjmp -#endif - -typedef struct thread { - struct thread * next; // Next thread - int flags; // Flags - #define FLG_THD_ALLOC 0x0001 - #define FLG_THD_MAIN 0x0002 - #define FLG_THD_DEAD 0x0004 - #define FLG_THD_WAIT 0x0008 - size_t size; // Size of the thread stack (including this structure) - threadreturn_t (*fn)(void *param); // Thread function - void * param; // Parameter for the thread function - jmp_buf cxt; // The current thread context. -} thread; - -typedef struct threadQ { - thread *head; - thread *tail; -} threadQ; - -static threadQ readyQ; // The list of ready threads -static threadQ deadQ; // Where we put threads waiting to be deallocated -static thread * current; // The current running thread -static thread mainthread; // The main thread context - -static void Qinit(threadQ * q) { - q->head = q->tail = 0; -} - -static void Qadd(threadQ * q, thread *t) { - t->next = 0; - if (q->head) { - q->tail->next = t; - q->tail = t; - } else - q->head = q->tail = t; -} - -static thread *Qpop(threadQ * q) { - struct thread * t; - - if (!q->head) - return 0; - t = q->head; - q->head = t->next; - return t; -} - -#if AUTO_DETECT_MASK - // The structure for the saved stack frame information - typedef struct saveloc { - char * localptr; - jmp_buf cxt; - } saveloc; - - // A pointer to our auto-detection buffer. - static saveloc *pframeinfo; - - /* These functions are not static to prevent the compiler removing them as functions */ - - void get_stack_state(void) { - char* c; - pframeinfo->localptr = (char *)&c; - _setjmp(pframeinfo->cxt); - } - - void get_stack_state_in_fn(void) { - pframeinfo++; - get_stack_state(); - pframeinfo--; - } -#endif - -static void _gosThreadsInit(void) { - Qinit(&readyQ); - current = &mainthread; - current->next = 0; - current->size = sizeof(thread); - current->flags = FLG_THD_MAIN; - current->fn = 0; - current->param = 0; - - #if AUTO_DETECT_MASK - { - uint32_t i; - char ** pout; - char ** pin; - size_t diff; - char * framebase; - - // Allocate a buffer to store our test data - pframeinfo = gfxAlloc(sizeof(saveloc)*2); - - // Get details of the stack frame from within a function - get_stack_state_in_fn(); - - // Get details of the stack frame outside the function - get_stack_state(); - - /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */ - stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr; - pout = (char **)pframeinfo[0].cxt; - pin = (char **)pframeinfo[1].cxt; - diff = pframeinfo[0].localptr - pframeinfo[1].localptr; - framebase = pframeinfo[0].localptr; - jmpmask1 = jmpmask2 = 0; - for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { - if ((size_t)(*pout - *pin) == diff) { - if (i < 32) - jmpmask1 |= 1 << i; - else - jmpmask2 |= 1 << (i-32); - - if (stackdirup) { - if (framebase > *pout) - framebase = *pout; - } else { - if (framebase < *pout) - framebase = *pout; - } - } - } - stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr); - - // Clean up - gfxFree(pframeinfo); - } - #endif -} - -gfxThreadHandle gfxThreadMe(void) { - return (gfxThreadHandle)current; -} - -void gfxYield(void) { - if (!_setjmp(current->cxt)) { - // Add us back to the Queue - Qadd(&readyQ, current); - - // Check if there are dead processes to deallocate - while ((current = Qpop(&deadQ))) - gfxFree(current); - - // Run the next process - current = Qpop(&readyQ); - _longjmp(current->cxt, 1); - } -} - -// This routine is not currently public - but it could be. -void gfxThreadExit(threadreturn_t ret) { - // Save the results - current->param = (void *)ret; - current->flags |= FLG_THD_DEAD; - - // Add us to the dead list if we need deallocation as we can't free ourselves. - // If someone is waiting on the thread they will do the cleanup. - if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) - Qadd(&deadQ, current); - - // Switch to the next task - current = Qpop(&readyQ); - if (!current) - gfxExit(); // Oops - this should never happen! - _longjmp(current->cxt, 1); -} - -gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { - thread * t; - (void) prio; - - // Ensure we have a minimum stack size - if (stacksz < sizeof(thread)+64) { - stacksz = sizeof(thread)+64; - stackarea = 0; - } - - if (stackarea) { - t = (thread *)stackarea; - t->flags = 0; - } else { - t = (thread *)gfxAlloc(stacksz); - if (!t) - return 0; - t->flags = FLG_THD_ALLOC; - } - t->size = stacksz; - t->fn = fn; - t->param = param; - if (_setjmp(t->cxt)) { - // This is the new thread - call the function! - gfxThreadExit(current->fn(current->param)); - - // We never get here - return 0; - } - - // Move the stack frame and relocate the context data - { - char ** s; - char * nf; - int diff; - uint32_t i; - - // Copy the stack frame - #if AUTO_DETECT_MASK - if (STACK_DIR_UP) { // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); - } else { // Stack grows down - nf = (char *)(t) + stacksz - (stackbase + sizeof(char *)); - memcpy(nf, &t, stackbase+sizeof(char *)); - } - #elif STACK_DIR_UP - // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); - #else - // Stack grows down - nf = (char *)(t) + size - (stackbase + sizeof(char *)); - memcpy(nf, &t, stackbase+sizeof(char *)); - #endif - - // Relocate the context data - s = (char **)(t->cxt); - diff = nf - (char *)&t; - - // Relocate the elements we know need to be relocated - for (i = 1; i && i < MASK1; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #ifdef MASK2 - for (i = 1; i && i < MASK2; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #endif - } - - // Add this thread to the ready queue - Qadd(&readyQ, t); - return t; -} - -threadreturn_t gfxThreadWait(gfxThreadHandle th) { - thread * t; - - t = th; - if (t == current) - return -1; - - // Mark that we are waiting - t->flags |= FLG_THD_WAIT; - - // Wait for the thread to die - while(!(t->flags & FLG_THD_DEAD)) - gfxYield(); - - // Unmark - t->flags &= ~FLG_THD_WAIT; - - // Clean up resources if needed - if (t->flags & FLG_THD_ALLOC) - gfxFree(t); - - // Return the status left by the dead process - return (threadreturn_t)t->param; +systemticks_t gfxMillisecondsToTicks(delaytime_t ms) { + return ms; } -#endif /* GFX_USE_OS_RAW32 */ +#endif /* GFX_USE_OS_ARDUINO */ diff --git a/src/gos/gos_arduino.h b/src/gos/gos_arduino.h index fc9e7073..6a18aaec 100644 --- a/src/gos/gos_arduino.h +++ b/src/gos/gos_arduino.h @@ -44,70 +44,26 @@ typedef bool bool_t; typedef uint32_t size_t; #endif -typedef uint32_t delaytime_t; -typedef uint32_t systemticks_t; -typedef short semcount_t; -typedef int threadreturn_t; -typedef int threadpriority_t; - -#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) -#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; - -#define TIME_IMMEDIATE 0 -#define TIME_INFINITE ((delaytime_t)-1) -#define MAX_SEMAPHORE_COUNT 0x7FFF -#define LOW_PRIORITY 0 -#define NORMAL_PRIORITY 1 -#define HIGH_PRIORITY 2 - -typedef struct { - semcount_t cnt; - semcount_t limit; -} gfxSem; - -typedef uint32_t gfxMutex; -typedef void * gfxThreadHandle; - -#define gfxThreadClose(thread) -#define gfxMutexDestroy(pmutex) -#define gfxSemDestroy(psem) -#define gfxSemCounter(psem) ((psem)->cnt) -#define gfxSemCounterI(psem) ((psem)->cnt) - -#define gfxSystemTicks() millis() -#define gfxMillisecondsToTicks(ms) (ms) - #ifdef __cplusplus extern "C" { #endif void gfxHalt(const char *msg); void gfxExit(void); - void *gfxAlloc(size_t sz); - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); - void gfxFree(void *ptr); - void gfxYield(void); - void gfxSleepMilliseconds(delaytime_t ms); - void gfxSleepMicroseconds(delaytime_t ms); - //systemticks_t gfxSystemTicks(void); - //systemticks_t gfxMillisecondsToTicks(delaytime_t ms); - void gfxSystemLock(void); - void gfxSystemUnlock(void); - void gfxMutexInit(gfxMutex *pmutex); - void gfxMutexEnter(gfxMutex *pmutex); - void gfxMutexExit(gfxMutex *pmutex); - void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); - bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); - bool_t gfxSemWaitI(gfxSem *psem); - void gfxSemSignal(gfxSem *psem); - void gfxSemSignalI(gfxSem *psem); - gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); - threadreturn_t gfxThreadWait(gfxThreadHandle thread); - gfxThreadHandle gfxThreadMe(void); #ifdef __cplusplus } #endif +/*===========================================================================*/ +/* Use the generic thread handling and heap handling */ +/*===========================================================================*/ + +#define GOS_NEED_X_THREADS TRUE +#define GOS_NEED_X_HEAP TRUE + +#include "gos_x_threads.h" +#include "gos_x_heap.h" + #endif /* GFX_USE_OS_ARDUINO */ #endif /* _GOS_ARDUINO_H */ diff --git a/src/gos/gos_chibios.c b/src/gos/gos_chibios.c index 62f5d8ee..4c79bd3d 100644 --- a/src/gos/gos_chibios.c +++ b/src/gos/gos_chibios.c @@ -48,7 +48,7 @@ void _gosInit(void) chSysInit(); } #endif - #else + #elif !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call halInit() and chSysInit() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_ecos.c b/src/gos/gos_ecos.c index 16ce821b..cdad86da 100644 --- a/src/gos/gos_ecos.c +++ b/src/gos/gos_ecos.c @@ -13,7 +13,8 @@ void _gosInit(void) { #if !GFX_NO_OS_INIT #error "GOS: Operating System initialization for eCos is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" - #else + #endif + #if !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call cyg_scheduler_start() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_freertos.c b/src/gos/gos_freertos.c index dbdfd22e..7b978738 100644 --- a/src/gos/gos_freertos.c +++ b/src/gos/gos_freertos.c @@ -26,7 +26,8 @@ void _gosInit(void) { #if !GFX_NO_OS_INIT #error "GOS: Operating System initialization for FreeRTOS is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" - #else + #endif + #if !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call vTaskStartScheduler() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_mk.c b/src/gos/gos_mk.c index 71267233..1b033206 100644 --- a/src/gos/gos_mk.c +++ b/src/gos/gos_mk.c @@ -14,3 +14,5 @@ #include "gos_raw32.c" #include "gos_rawrtos.c" #include "gos_win32.c" +#include "gos_x_threads.c" +#include "gos_x_heap.c" diff --git a/src/gos/gos_options.h b/src/gos/gos_options.h index 8fb4f51b..600c3085 100644 --- a/src/gos/gos_options.h +++ b/src/gos/gos_options.h @@ -74,7 +74,7 @@ * @details Defaults to FALSE */ #ifndef GFX_USE_OS_ARDUINO - #define GFX_USE_OS_ARDUINO FALSE + #define GFX_USE_OS_ARDUINO FALSE #endif /** * @} @@ -88,12 +88,35 @@ * @note This is setting enables optimisations that are compiler specific. It does * not need to be specified as reasonable defaults and various auto-detection * will happen as required. + * @note Currently only used by ugfx generic thread handling (GOS_USE_OS_RAW32 and GOS_USE_OS_ARDUINO) */ #ifndef GFX_COMPILER #define GFX_COMPILER GFX_COMPILER_UNKNOWN #endif #define GFX_COMPILER_UNKNOWN 0 // Unknown compiler #define GFX_COMPILER_MINGW32 1 // MingW32 (x86) compiler for windows + /** + * @brief Enable cpu specific code + * @details Defaults to GFX_CPU_UNKNOWN + * @note This is setting enables optimisations that are cpu specific. It does + * not need to be specified as reasonable defaults and various auto-detection + * will happen as required. + * @note Currently only used by ugfx generic thread handling (GOS_USE_OS_RAW32 and GOS_USE_OS_ARDUINO) + * @{ + */ + #ifndef GFX_CPU + #define GFX_CPU GFX_CPU_UNKNOWN + #endif + #define GFX_CPU_UNKNOWN 0 //**< Unknown cpu + #define GFX_CPU_CORTEX_M0 1 //**< Cortex M0 + #define GFX_CPU_CORTEX_M1 2 //**< Cortex M1 + #define GFX_CPU_CORTEX_M2 3 //**< Cortex M2 + #define GFX_CPU_CORTEX_M3 4 //**< Cortex M3 + #define GFX_CPU_CORTEX_M4 5 //**< Cortex M4 + #define GFX_CPU_CORTEX_M4_FP 6 //**< Cortex M4 with hardware floating point + #define GFX_CPU_CORTEX_M7 7 //**< Cortex M7 + #define GFX_CPU_CORTEX_M7_FP 8 //**< Cortex M7 with hardware floating point + /** @} */ /** * @brief Should uGFX avoid initializing the operating system * @details Defaults to FALSE @@ -109,6 +132,16 @@ #define GFX_NO_OS_INIT FALSE #endif /** + * @brief Turn off warnings about initializing the operating system + * @details Defaults to FALSE + * @note This is only relevant where GOS cannot initialise the operating + * system automatically or the operating system initialisation has been + * explicitly turned off. + */ + #ifndef GFX_OS_INIT_NO_WARNING + #define GFX_OS_INIT_NO_WARNING FALSE + #endif + /** * @brief Should uGFX stuff be added to the FreeRTOS+Tracer * @details Defaults to FALSE */ @@ -117,7 +150,8 @@ #endif /** * @brief How much RAM should uGFX use for the heap - * @details Defaults to 0. Only valid with GFX_USE_OS_RAW32 + * @details Defaults to 0. + * @note Only used when the generic ugfx heap code is used (GFX_USE_OS_RAW32 and GFX_USE_OS_ARDUINO) * @note If 0 then the standard C runtime malloc(), free() and realloc() * are used. * @note If it is non-zero then this is the number of bytes of RAM @@ -125,8 +159,8 @@ * runtime routines will be used and a new routine @p gfxAddHeapBlock() * is added allowing the user to add extra memory blocks to the heap. */ - #ifndef GOS_RAW_HEAP_SIZE - #define GOS_RAW_HEAP_SIZE 0 + #ifndef GFX_OS_HEAP_SIZE + #define GFX_OS_HEAP_SIZE 0 #endif /** @} */ diff --git a/src/gos/gos_raw32.c b/src/gos/gos_raw32.c index c454a68b..4e61feb9 100644 --- a/src/gos/gos_raw32.c +++ b/src/gos/gos_raw32.c @@ -12,14 +12,8 @@ #if GFX_USE_OS_RAW32 -#include <string.h> // Prototype for memcpy() - -#if GOS_RAW_HEAP_SIZE != 0 - static void _gosHeapInit(void); -#else - #define _gosHeapInit() -#endif -static void _gosThreadsInit(void); +void _gosHeapInit(void); +void _gosThreadsInit(void); /********************************************************* * Initialise @@ -31,7 +25,9 @@ void _gosInit(void) * On the other hand the C runtime should still already be initialized before * getting here! */ - #warning "GOS: Raw32 - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #if !GFX_OS_INIT_NO_WARNING + #warning "GOS: Raw32 - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #endif // Set up the heap allocator _gosHeapInit(); @@ -46,7 +42,7 @@ void _gosDeinit(void) } /********************************************************* - * For WIn32 emulation - automatically add the tick functions + * For Win32 emulation - automatically add the tick functions * the user would normally have to provide for bare metal. *********************************************************/ @@ -98,651 +94,4 @@ void gfxExit(void) { #endif } -/********************************************************* - * Head allocation functions - *********************************************************/ - -#if GOS_RAW_HEAP_SIZE == 0 - #include <stdlib.h> // Prototype for malloc(), realloc() and free() - - void *gfxAlloc(size_t sz) { - return malloc(sz); - } - - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { - (void) oldsz; - return realloc(ptr, newsz); - } - - void gfxFree(void *ptr) { - free(ptr); - } - -#else - - // Slot structure - user memory follows - typedef struct memslot { - struct memslot *next; // The next memslot - size_t sz; // Includes the size of this memslot. - } memslot; - - // Free Slot - immediately follows the memslot structure - typedef struct freeslot { - memslot *nextfree; // The next free slot - } freeslot; - - #define GetSlotSize(sz) ((((sz) + (sizeof(freeslot) - 1)) & ~(sizeof(freeslot) - 1)) + sizeof(memslot)) - #define NextFree(pslot) ((freeslot *)Slot2Ptr(pslot))->nextfree - #define Ptr2Slot(p) ((memslot *)(p) - 1) - #define Slot2Ptr(pslot) ((pslot)+1) - - static memslot * firstSlot; - static memslot * lastSlot; - static memslot * freeSlots; - static char heap[GOS_RAW_HEAP_SIZE]; - - static void _gosHeapInit(void) { - lastSlot = 0; - gfxAddHeapBlock(heap, GOS_RAW_HEAP_SIZE); - } - - void gfxAddHeapBlock(void *ptr, size_t sz) { - if (sz < sizeof(memslot)+sizeof(freeslot)) - return; - - if (lastSlot) - lastSlot->next = (memslot *)ptr; - else - firstSlot = lastSlot = freeSlots = (memslot *)ptr; - - lastSlot->next = 0; - lastSlot->sz = sz; - NextFree(lastSlot) = 0; - } - - void *gfxAlloc(size_t sz) { - register memslot *prev, *p, *new; - - if (!sz) return 0; - sz = GetSlotSize(sz); - for (prev = 0, p = freeSlots; p != 0; prev = p, p = NextFree(p)) { - // Loop till we have a block big enough - if (p->sz < sz) - continue; - // Can we save some memory by splitting this block? - if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { - new = (memslot *)((char *)p + sz); - new->next = p->next; - p->next = new; - new->sz = p->sz - sz; - p->sz = sz; - if (lastSlot == p) - lastSlot = new; - NextFree(new) = NextFree(p); - NextFree(p) = new; - } - // Remove it from the free list - if (prev) - NextFree(prev) = NextFree(p); - else - freeSlots = NextFree(p); - // Return the result found - return Slot2Ptr(p); - } - // No slots large enough - return 0; - } - - void *gfxRealloc(void *ptr, size_t oldsz, size_t sz) { - register memslot *prev, *p, *new; - (void) oldsz; - - if (!ptr) - return gfxAlloc(sz); - if (!sz) { - gfxFree(ptr); - return 0; - } - - p = Ptr2Slot(ptr); - sz = GetSlotSize(sz); - - // If the next slot is free (and contiguous) merge it into this one - if ((char *)p + p->sz == (char *)p->next) { - for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { - if (new == p->next) { - p->next = new->next; - p->sz += new->sz; - if (prev) - NextFree(prev) = NextFree(new); - else - freeSlots = NextFree(new); - if (lastSlot == new) - lastSlot = p; - break; - } - } - } - - // If this block is large enough we are nearly done - if (sz < p->sz) { - // Can we save some memory by splitting this block? - if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { - new = (memslot *)((char *)p + sz); - new->next = p->next; - p->next = new; - new->sz = p->sz - sz; - p->sz = sz; - if (lastSlot == p) - lastSlot = new; - NextFree(new) = freeSlots; - freeSlots = new; - } - return Slot2Ptr(p); - } - - // We need to do this the hard way - if ((new = gfxAlloc(sz))) - return 0; - memcpy(new, ptr, p->sz - sizeof(memslot)); - gfxFree(ptr); - return new; - } - - void gfxFree(void *ptr) { - register memslot *prev, *p, *new; - - if (!ptr) - return; - - p = Ptr2Slot(ptr); - - // If the next slot is free (and contiguous) merge it into this one - if ((char *)p + p->sz == (char *)p->next) { - for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { - if (new == p->next) { - p->next = new->next; - p->sz += new->sz; - if (prev) - NextFree(prev) = NextFree(new); - else - freeSlots = NextFree(new); - if (lastSlot == new) - lastSlot = p; - break; - } - } - } - - // Add it into the free chain - NextFree(p) = freeSlots; - freeSlots = p; - } -#endif - -/********************************************************* - * Semaphores and critical region functions - *********************************************************/ - -#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) - #define INTERRUPTS_OFF() - #define INTERRUPTS_ON() -#endif - -void gfxSystemLock(void) { - INTERRUPTS_OFF(); -} - -void gfxSystemUnlock(void) { - INTERRUPTS_ON(); -} - -void gfxMutexInit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxMutexEnter(gfxMutex *pmutex) { - INTERRUPTS_OFF(); - while (pmutex[0]) { - INTERRUPTS_ON(); - gfxYield(); - INTERRUPTS_OFF(); - } - pmutex[0] = 1; - INTERRUPTS_ON(); -} - -void gfxMutexExit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { - psem->cnt = val; - psem->limit = limit; -} - -bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { - systemticks_t starttm, delay; - - // Convert our delay to ticks - switch (ms) { - case TIME_IMMEDIATE: - delay = TIME_IMMEDIATE; - break; - case TIME_INFINITE: - delay = TIME_INFINITE; - break; - default: - delay = gfxMillisecondsToTicks(ms); - if (!delay) delay = 1; - starttm = gfxSystemTicks(); - } - - INTERRUPTS_OFF(); - while (psem->cnt <= 0) { - INTERRUPTS_ON(); - // Check if we have exceeded the defined delay - switch (delay) { - case TIME_IMMEDIATE: - return FALSE; - case TIME_INFINITE: - break; - default: - if (gfxSystemTicks() - starttm >= delay) - return FALSE; - break; - } - gfxYield(); - INTERRUPTS_OFF(); - } - psem->cnt--; - INTERRUPTS_ON(); - return TRUE; -} - -bool_t gfxSemWaitI(gfxSem *psem) { - if (psem->cnt <= 0) - return FALSE; - psem->cnt--; - return TRUE; -} - -void gfxSemSignal(gfxSem *psem) { - INTERRUPTS_OFF(); - gfxSemSignalI(psem); - INTERRUPTS_ON(); -} - -void gfxSemSignalI(gfxSem *psem) { - if (psem->cnt < psem->limit) - psem->cnt++; -} - -/********************************************************* - * Sleep functions - *********************************************************/ - -void gfxSleepMilliseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); -} - -void gfxSleepMicroseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms/1000); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); -} - -/********************************************************* - * Threading functions - *********************************************************/ - -/** - * There are some compilers we know how they store the jmpbuf. For those - * we can use the constant macro definitions. For others we have to "auto-detect". - * Auto-detection is hairy and there is no guarantee it will work on all architectures. - * For those it doesn't - read the compiler manuals and the library source code to - * work out the correct macro values. - * You can use the debugger to work out the values for your compiler and put them here. - * Defining these macros as constant values makes the system behavior guaranteed but also - * makes your code compiler and cpu architecture dependent. It also saves a heap of code - * and a few bytes of RAM. - */ -#if GFX_COMPILER == GFX_COMPILER_MINGW32 - #define AUTO_DETECT_MASK FALSE - #define STACK_DIR_UP FALSE - #define MASK1 0x00000011 - #define MASK2 0x00000000 - #define STACK_BASE 12 -#else - // Use auto-detection of the stack frame format - // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf. - #define AUTO_DETECT_MASK TRUE - #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down - #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation - #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation - #define STACK_BASE stackbase // The base of the stack frame relative to the local variables - static bool_t stackdirup; - static uint32_t jmpmask1; - static uint32_t jmpmask2; - static size_t stackbase; -#endif - -#include <setjmp.h> /* jmp_buf, setjmp(), longjmp() */ - -/** - * Some compilers define a _setjmp() and a setjmp(). - * The difference between them is that setjmp() saves the signal masks. - * That is of no use to us so prefer to use the _setjmp() methods. - * If they don't exist compile them to be the standard setjmp() function. - * Similarly for longjmp(). - */ -#if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__) - #define _setjmp setjmp -#endif -#if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__) - #define _longjmp longjmp -#endif - -typedef struct thread { - struct thread * next; // Next thread - int flags; // Flags - #define FLG_THD_ALLOC 0x0001 - #define FLG_THD_MAIN 0x0002 - #define FLG_THD_DEAD 0x0004 - #define FLG_THD_WAIT 0x0008 - size_t size; // Size of the thread stack (including this structure) - threadreturn_t (*fn)(void *param); // Thread function - void * param; // Parameter for the thread function - jmp_buf cxt; // The current thread context. -} thread; - -typedef struct threadQ { - thread *head; - thread *tail; -} threadQ; - -static threadQ readyQ; // The list of ready threads -static threadQ deadQ; // Where we put threads waiting to be deallocated -static thread * current; // The current running thread -static thread mainthread; // The main thread context - -static void Qinit(threadQ * q) { - q->head = q->tail = 0; -} - -static void Qadd(threadQ * q, thread *t) { - t->next = 0; - if (q->head) { - q->tail->next = t; - q->tail = t; - } else - q->head = q->tail = t; -} - -static thread *Qpop(threadQ * q) { - struct thread * t; - - if (!q->head) - return 0; - t = q->head; - q->head = t->next; - return t; -} - -#if AUTO_DETECT_MASK - // The structure for the saved stack frame information - typedef struct saveloc { - char * localptr; - jmp_buf cxt; - } saveloc; - - // A pointer to our auto-detection buffer. - static saveloc *pframeinfo; - - /* These functions are not static to prevent the compiler removing them as functions */ - - void get_stack_state(void) { - char* c; - pframeinfo->localptr = (char *)&c; - _setjmp(pframeinfo->cxt); - } - - void get_stack_state_in_fn(void) { - pframeinfo++; - get_stack_state(); - pframeinfo--; - } -#endif - -static void _gosThreadsInit(void) { - Qinit(&readyQ); - current = &mainthread; - current->next = 0; - current->size = sizeof(thread); - current->flags = FLG_THD_MAIN; - current->fn = 0; - current->param = 0; - - #if AUTO_DETECT_MASK - { - uint32_t i; - char ** pout; - char ** pin; - size_t diff; - char * framebase; - - // Allocate a buffer to store our test data - pframeinfo = gfxAlloc(sizeof(saveloc)*2); - - // Get details of the stack frame from within a function - get_stack_state_in_fn(); - - // Get details of the stack frame outside the function - get_stack_state(); - - /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */ - stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr; - pout = (char **)pframeinfo[0].cxt; - pin = (char **)pframeinfo[1].cxt; - diff = pframeinfo[0].localptr - pframeinfo[1].localptr; - framebase = pframeinfo[0].localptr; - jmpmask1 = jmpmask2 = 0; - for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { - if ((size_t)(*pout - *pin) == diff) { - if (i < 32) - jmpmask1 |= 1 << i; - else - jmpmask2 |= 1 << (i-32); - - if (stackdirup) { - if (framebase > *pout) - framebase = *pout; - } else { - if (framebase < *pout) - framebase = *pout; - } - } - } - stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr); - - // Clean up - gfxFree(pframeinfo); - } - #endif -} - -gfxThreadHandle gfxThreadMe(void) { - return (gfxThreadHandle)current; -} - -void gfxYield(void) { - if (!_setjmp(current->cxt)) { - // Add us back to the Queue - Qadd(&readyQ, current); - - // Check if there are dead processes to deallocate - while ((current = Qpop(&deadQ))) - gfxFree(current); - - // Run the next process - current = Qpop(&readyQ); - _longjmp(current->cxt, 1); - } -} - -// This routine is not currently public - but it could be. -void gfxThreadExit(threadreturn_t ret) { - // Save the results - current->param = (void *)ret; - current->flags |= FLG_THD_DEAD; - - // Add us to the dead list if we need deallocation as we can't free ourselves. - // If someone is waiting on the thread they will do the cleanup. - if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) - Qadd(&deadQ, current); - - // Switch to the next task - current = Qpop(&readyQ); - if (!current) - gfxExit(); // Oops - this should never happen! - _longjmp(current->cxt, 1); -} - -gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { - thread * t; - (void) prio; - - // Ensure we have a minimum stack size - if (stacksz < sizeof(thread)+64) { - stacksz = sizeof(thread)+64; - stackarea = 0; - } - - if (stackarea) { - t = (thread *)stackarea; - t->flags = 0; - } else { - t = (thread *)gfxAlloc(stacksz); - if (!t) - return 0; - t->flags = FLG_THD_ALLOC; - } - t->size = stacksz; - t->fn = fn; - t->param = param; - if (_setjmp(t->cxt)) { - // This is the new thread - call the function! - gfxThreadExit(current->fn(current->param)); - - // We never get here - return 0; - } - - // Move the stack frame and relocate the context data - { - char ** s; - char * nf; - int diff; - uint32_t i; - - // Copy the stack frame - #if AUTO_DETECT_MASK - if (STACK_DIR_UP) { // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); - } else { // Stack grows down - nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); - memcpy(nf, &t, stackbase+sizeof(char *)); - } - #elif STACK_DIR_UP - // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); - #else - // Stack grows down - nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); - memcpy(nf, &t, stackbase+sizeof(char *)); - #endif - - // Relocate the context data - s = (char **)(t->cxt); - diff = nf - (char *)&t; - - // Relocate the elements we know need to be relocated - for (i = 1; i && i < MASK1; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #ifdef MASK2 - for (i = 1; i && i < MASK2; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #endif - } - - // Add this thread to the ready queue - Qadd(&readyQ, t); - return t; -} - -threadreturn_t gfxThreadWait(gfxThreadHandle th) { - thread * t; - - t = th; - if (t == current) - return -1; - - // Mark that we are waiting - t->flags |= FLG_THD_WAIT; - - // Wait for the thread to die - while(!(t->flags & FLG_THD_DEAD)) - gfxYield(); - - // Unmark - t->flags &= ~FLG_THD_WAIT; - - // Clean up resources if needed - if (t->flags & FLG_THD_ALLOC) - gfxFree(t); - - // Return the status left by the dead process - return (threadreturn_t)t->param; -} - #endif /* GFX_USE_OS_RAW32 */ diff --git a/src/gos/gos_raw32.h b/src/gos/gos_raw32.h index a37b78ff..0fca9223 100644 --- a/src/gos/gos_raw32.h +++ b/src/gos/gos_raw32.h @@ -26,18 +26,6 @@ #if GFX_USE_OS_RAW32 /*===========================================================================*/ -/* Special Macros just for a Raw implementation */ -/*===========================================================================*/ - -/** - * @brief Set the maximum size of the heap. - * @note If set to 0 then the C runtime library malloc() and free() are used. - */ -#ifndef GOS_RAW_HEAP_SIZE - #define GOS_RAW_HEAP_SIZE 0 -#endif - -/*===========================================================================*/ /* Type definitions */ /*===========================================================================*/ @@ -60,71 +48,26 @@ typedef unsigned char bool_t; typedef uint32_t size_t; #endif -typedef uint32_t delaytime_t; -typedef uint32_t systemticks_t; -typedef short semcount_t; -typedef int threadreturn_t; -typedef int threadpriority_t; - -#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) -#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; - -#define TIME_IMMEDIATE 0 -#define TIME_INFINITE ((delaytime_t)-1) -#define MAX_SEMAPHORE_COUNT 0x7FFF -#define LOW_PRIORITY 0 -#define NORMAL_PRIORITY 1 -#define HIGH_PRIORITY 2 - -typedef struct { - semcount_t cnt; - semcount_t limit; -} gfxSem; - -typedef uint32_t gfxMutex; -typedef void * gfxThreadHandle; - -#define gfxThreadClose(thread) -#define gfxMutexDestroy(pmutex) -#define gfxSemDestroy(psem) -#define gfxSemCounter(psem) ((psem)->cnt) -#define gfxSemCounterI(psem) ((psem)->cnt) - #ifdef __cplusplus extern "C" { #endif - #if GOS_RAW_HEAP_SIZE != 0 - void gfxAddHeapBlock(void *ptr, size_t sz); - #endif - void gfxHalt(const char *msg); void gfxExit(void); - void *gfxAlloc(size_t sz); - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); - void gfxFree(void *ptr); - void gfxYield(void); - void gfxSleepMilliseconds(delaytime_t ms); - void gfxSleepMicroseconds(delaytime_t ms); - systemticks_t gfxSystemTicks(void); - systemticks_t gfxMillisecondsToTicks(delaytime_t ms); - void gfxSystemLock(void); - void gfxSystemUnlock(void); - void gfxMutexInit(gfxMutex *pmutex); - void gfxMutexEnter(gfxMutex *pmutex); - void gfxMutexExit(gfxMutex *pmutex); - void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); - bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); - bool_t gfxSemWaitI(gfxSem *psem); - void gfxSemSignal(gfxSem *psem); - void gfxSemSignalI(gfxSem *psem); - gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); - threadreturn_t gfxThreadWait(gfxThreadHandle thread); - gfxThreadHandle gfxThreadMe(void); #ifdef __cplusplus } #endif +/*===========================================================================*/ +/* Use the generic thread handling and heap handling */ +/*===========================================================================*/ + +#define GOS_NEED_X_THREADS TRUE +#define GOS_NEED_X_HEAP TRUE + +#include "gos_x_threads.h" +#include "gos_x_heap.h" + #endif /* GFX_USE_OS_RAW32 */ #endif /* _GOS_RAW32_H */ diff --git a/src/gos/gos_rawrtos.c b/src/gos/gos_rawrtos.c index c47c85bf..8efe2235 100644 --- a/src/gos/gos_rawrtos.c +++ b/src/gos/gos_rawrtos.c @@ -26,7 +26,8 @@ void _gosInit(void) { #if !GFX_NO_OS_INIT #error "GOS: Operating System initialization for RawRTOS is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" - #else + #endif + #if !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call raw_os_start() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_x_heap.c b/src/gos/gos_x_heap.c new file mode 100644 index 00000000..94b74d37 --- /dev/null +++ b/src/gos/gos_x_heap.c @@ -0,0 +1,195 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GOS_NEED_X_HEAP + +#include <string.h> // Prototype for memcpy() + + +#if GFX_OS_HEAP_SIZE == 0 + #include <stdlib.h> // Prototype for malloc(), realloc() and free() + + void _gosHeapInit(void) { + } + void *gfxAlloc(size_t sz) { + return malloc(sz); + } + + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { + (void) oldsz; + return realloc(ptr, newsz); + } + + void gfxFree(void *ptr) { + free(ptr); + } + +#else + + // Slot structure - user memory follows + typedef struct memslot { + struct memslot *next; // The next memslot + size_t sz; // Includes the size of this memslot. + } memslot; + + // Free Slot - immediately follows the memslot structure + typedef struct freeslot { + memslot *nextfree; // The next free slot + } freeslot; + + #define GetSlotSize(sz) ((((sz) + (sizeof(freeslot) - 1)) & ~(sizeof(freeslot) - 1)) + sizeof(memslot)) + #define NextFree(pslot) ((freeslot *)Slot2Ptr(pslot))->nextfree + #define Ptr2Slot(p) ((memslot *)(p) - 1) + #define Slot2Ptr(pslot) ((pslot)+1) + + static memslot * firstSlot; + static memslot * lastSlot; + static memslot * freeSlots; + static char heap[GFX_OS_HEAP_SIZE]; + + void _gosHeapInit(void) { + lastSlot = 0; + gfxAddHeapBlock(heap, GFX_OS_HEAP_SIZE); + } + + void gfxAddHeapBlock(void *ptr, size_t sz) { + if (sz < sizeof(memslot)+sizeof(freeslot)) + return; + + if (lastSlot) + lastSlot->next = (memslot *)ptr; + else + firstSlot = lastSlot = freeSlots = (memslot *)ptr; + + lastSlot->next = 0; + lastSlot->sz = sz; + NextFree(lastSlot) = 0; + } + + void *gfxAlloc(size_t sz) { + register memslot *prev, *p, *new; + + if (!sz) return 0; + sz = GetSlotSize(sz); + for (prev = 0, p = freeSlots; p != 0; prev = p, p = NextFree(p)) { + // Loop till we have a block big enough + if (p->sz < sz) + continue; + // Can we save some memory by splitting this block? + if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { + new = (memslot *)((char *)p + sz); + new->next = p->next; + p->next = new; + new->sz = p->sz - sz; + p->sz = sz; + if (lastSlot == p) + lastSlot = new; + NextFree(new) = NextFree(p); + NextFree(p) = new; + } + // Remove it from the free list + if (prev) + NextFree(prev) = NextFree(p); + else + freeSlots = NextFree(p); + // Return the result found + return Slot2Ptr(p); + } + // No slots large enough + return 0; + } + + void *gfxRealloc(void *ptr, size_t oldsz, size_t sz) { + register memslot *prev, *p, *new; + (void) oldsz; + + if (!ptr) + return gfxAlloc(sz); + if (!sz) { + gfxFree(ptr); + return 0; + } + + p = Ptr2Slot(ptr); + sz = GetSlotSize(sz); + + // If the next slot is free (and contiguous) merge it into this one + if ((char *)p + p->sz == (char *)p->next) { + for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { + if (new == p->next) { + p->next = new->next; + p->sz += new->sz; + if (prev) + NextFree(prev) = NextFree(new); + else + freeSlots = NextFree(new); + if (lastSlot == new) + lastSlot = p; + break; + } + } + } + + // If this block is large enough we are nearly done + if (sz < p->sz) { + // Can we save some memory by splitting this block? + if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { + new = (memslot *)((char *)p + sz); + new->next = p->next; + p->next = new; + new->sz = p->sz - sz; + p->sz = sz; + if (lastSlot == p) + lastSlot = new; + NextFree(new) = freeSlots; + freeSlots = new; + } + return Slot2Ptr(p); + } + + // We need to do this the hard way + if ((new = gfxAlloc(sz))) + return 0; + memcpy(new, ptr, p->sz - sizeof(memslot)); + gfxFree(ptr); + return new; + } + + void gfxFree(void *ptr) { + register memslot *prev, *p, *new; + + if (!ptr) + return; + + p = Ptr2Slot(ptr); + + // If the next slot is free (and contiguous) merge it into this one + if ((char *)p + p->sz == (char *)p->next) { + for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { + if (new == p->next) { + p->next = new->next; + p->sz += new->sz; + if (prev) + NextFree(prev) = NextFree(new); + else + freeSlots = NextFree(new); + if (lastSlot == new) + lastSlot = p; + break; + } + } + } + + // Add it into the free chain + NextFree(p) = freeSlots; + freeSlots = p; + } +#endif + +#endif /* GOS_NEED_X_HEAP */ diff --git a/src/gos/gos_x_heap.h b/src/gos/gos_x_heap.h new file mode 100644 index 00000000..3612989c --- /dev/null +++ b/src/gos/gos_x_heap.h @@ -0,0 +1,62 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * The raw32 GOS implementation supports any 32 bit processor with or without an + * underlying operating system. It uses cooperative multi-tasking. Be careful + * when writing device drivers not to disturb the assumptions this creates by performing + * call-backs to uGFX code unless you define the INTERRUPTS_OFF() and INTERRUPTS_ON() macros. + * It still requires some C runtime library support... + * enough startup to initialise the stack, interrupts, static data etc and call main(). + * setjmp() and longjmp() - for threading + * memcpy() - for heap and threading + * malloc(), realloc and free() - if GFX_OS_HEAP_SIZE == 0 + * + * You must also define the following routines in your own code so that timing functions will work... + * systemticks_t gfxSystemTicks(void); + * systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + */ +#ifndef _GOS_X_HEAP_H +#define _GOS_X_HEAP_H + +#if GOS_NEED_X_HEAP + + +/*===========================================================================*/ +/* Special Macros */ +/*===========================================================================*/ + +/** + * @brief Set the maximum size of the heap. + * @note If set to 0 then the C runtime library malloc() and free() are used. + */ +#ifndef GFX_OS_HEAP_SIZE + #define GFX_OS_HEAP_SIZE 0 +#endif + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + #if GFX_OS_HEAP_SIZE != 0 + void gfxAddHeapBlock(void *ptr, size_t sz); + #endif + + void *gfxAlloc(size_t sz); + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); + void gfxFree(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* GOS_NEED_X_HEAP */ +#endif /* _GOS_X_HEAP_H */ diff --git a/src/gos/gos_x_threads.c b/src/gos/gos_x_threads.c new file mode 100644 index 00000000..8a781b21 --- /dev/null +++ b/src/gos/gos_x_threads.c @@ -0,0 +1,672 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GOS_NEED_X_THREADS + +/********************************************************* + * Semaphores and critical region functions + *********************************************************/ + +#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) + #define INTERRUPTS_OFF() + #define INTERRUPTS_ON() +#endif + +void gfxSystemLock(void) { + INTERRUPTS_OFF(); +} + +void gfxSystemUnlock(void) { + INTERRUPTS_ON(); +} + +void gfxMutexInit(gfxMutex *pmutex) { + pmutex[0] = 0; +} + +void gfxMutexEnter(gfxMutex *pmutex) { + INTERRUPTS_OFF(); + while (pmutex[0]) { + INTERRUPTS_ON(); + gfxYield(); + INTERRUPTS_OFF(); + } + pmutex[0] = 1; + INTERRUPTS_ON(); +} + +void gfxMutexExit(gfxMutex *pmutex) { + pmutex[0] = 0; +} + +void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { + psem->cnt = val; + psem->limit = limit; +} + +bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { + systemticks_t starttm, delay; + + // Convert our delay to ticks + starttm = 0; + switch (ms) { + case TIME_IMMEDIATE: + delay = TIME_IMMEDIATE; + break; + case TIME_INFINITE: + delay = TIME_INFINITE; + break; + default: + delay = gfxMillisecondsToTicks(ms); + if (!delay) delay = 1; + starttm = gfxSystemTicks(); + } + + INTERRUPTS_OFF(); + while (psem->cnt <= 0) { + INTERRUPTS_ON(); + // Check if we have exceeded the defined delay + switch (delay) { + case TIME_IMMEDIATE: + return FALSE; + case TIME_INFINITE: + break; + default: + if (gfxSystemTicks() - starttm >= delay) + return FALSE; + break; + } + gfxYield(); + INTERRUPTS_OFF(); + } + psem->cnt--; + INTERRUPTS_ON(); + return TRUE; +} + +bool_t gfxSemWaitI(gfxSem *psem) { + if (psem->cnt <= 0) + return FALSE; + psem->cnt--; + return TRUE; +} + +void gfxSemSignal(gfxSem *psem) { + INTERRUPTS_OFF(); + gfxSemSignalI(psem); + INTERRUPTS_ON(); +} + +void gfxSemSignalI(gfxSem *psem) { + if (psem->cnt < psem->limit) + psem->cnt++; +} + +/********************************************************* + * Sleep functions + *********************************************************/ + +void gfxSleepMilliseconds(delaytime_t ms) { + systemticks_t starttm, delay; + + // Safety first + switch (ms) { + case TIME_IMMEDIATE: + return; + case TIME_INFINITE: + while(1) + gfxYield(); + return; + } + + // Convert our delay to ticks + delay = gfxMillisecondsToTicks(ms); + starttm = gfxSystemTicks(); + + do { + gfxYield(); + } while (gfxSystemTicks() - starttm < delay); +} + +void gfxSleepMicroseconds(delaytime_t ms) { + systemticks_t starttm, delay; + + // Safety first + switch (ms) { + case TIME_IMMEDIATE: + return; + case TIME_INFINITE: + while(1) + gfxYield(); + return; + } + + // Convert our delay to ticks + delay = gfxMillisecondsToTicks(ms/1000); + starttm = gfxSystemTicks(); + + do { + gfxYield(); + } while (gfxSystemTicks() - starttm < delay); +} + +/********************************************************* + * Threading functions + *********************************************************/ + +/** For each scheduler the following need to be defined... + * + * void _gfxThreadsInit(void); - Initialise the scheduler + * void _gfxStartThread(thread *oldt, thread *newt); - Start a new thread + * void _gfxTaskSwitch(thread *oldt, thread *newt); - Switch to a different thread + * + */ + +typedef struct thread { + struct thread * next; // Next thread + int flags; // Flags + #define FLG_THD_ALLOC 0x0001 + #define FLG_THD_MAIN 0x0002 + #define FLG_THD_DEAD 0x0004 + #define FLG_THD_WAIT 0x0008 + size_t size; // Size of the thread stack (including this structure) + threadreturn_t (*fn)(void *param); // Thread function + void * param; // Parameter for the thread function + void * cxt; // The current thread context. + } thread; + +typedef struct threadQ { + thread *head; + thread *tail; +} threadQ; + +static threadQ readyQ; // The list of ready threads +static threadQ deadQ; // Where we put threads waiting to be deallocated +static thread * current; // The current running thread +static thread mainthread; // The main thread context + +#if GFX_CPU == GFX_CPU_UNKNOWN + + #include <string.h> // Prototype for memcpy() + #include <setjmp.h> + + /** + * Some compilers define a _setjmp() and a setjmp(). + * The difference between them is that setjmp() saves the signal masks. + * That is of no use to us so we prefer to use the _setjmp() methods. + * If they don't exist compile them to be the standard setjmp() function. + * Similarly for longjmp(). + */ + #if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__) + #define CXT_SAVE setjmp + #else + #define CXT_SAVE _setjmp + #endif + #if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__) + #define CXT_RESTORE longjmp + #else + #define CXT_RESTORE _longjmp + #endif + + // A place to store the main thread context. + // All other threads will store the context directly after the thread structure (as part of the stack space). + static jmp_buf maincxt; + + /** + * There are some compilers we know how they store the jmpbuf. For those + * we can use the constant macro definitions. For others we have to "auto-detect". + * Auto-detection is hairy and there is no guarantee it will work on all architectures. + * For those it doesn't - read the compiler manuals and the library source code to + * work out the correct macro values. + * You can use the debugger to work out the values for your compiler and put them here. + * Defining these macros as constant values makes the system behaviour guaranteed but also + * makes your code compiler and cpu architecture dependent. It also saves a heap of code + * and a few bytes of RAM. + * + * MACROS: + * + * AUTO_DETECT_STACKFRAME TRUE/FALSE - TRUE to auto-detect stack frame structure + * STACK_DIR_UP Macro/bool_t - TRUE if the stack grows up instead of down + * MASK1 Macro/uint32_t - The 1st mask of jmp_buf elements that need relocation + * MASK2 Macro/uint32_t - The 2nd mask of jmp_buf elements that need relocation + * STACK_BASE Macro/size_t - The base of the stack frame relative to the local variables + * _gfxThreadsInit() Macro/Function - Initialise the scheduler + * + */ + #if GFX_COMPILER == GFX_COMPILER_MINGW32 + + #define AUTO_DETECT_STACKFRAME FALSE + #define STACK_DIR_UP FALSE + #define MASK1 0x00000011 + #define MASK2 0x00000000 + #define STACK_BASE 12 + #define _gfxThreadsInit() mainthread.cxt = maincxt + + #else + + // Use auto-detection of the stack frame format + // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf. + #define AUTO_DETECT_STACKFRAME TRUE + #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down + #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation + #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation + #define STACK_BASE stackbase // The base of the stack frame relative to the local variables + + // The structure for the saved stack frame information + typedef struct saveloc { + char * localptr; + jmp_buf cxt; + } saveloc; + + static bool_t stackdirup; + static uint32_t jmpmask1; + static uint32_t jmpmask2; + static size_t stackbase; + static saveloc *pframeinfo; + + // These two functions are not static to prevent the compiler removing them as functions + void _gfxGetStackState(void) { + char *c; + pframeinfo->localptr = (char *)&c; + CXT_SAVE(pframeinfo->cxt); + } + void _gfxGetStackStateInFn(void) { + pframeinfo++; + _gfxGetStackState(); + pframeinfo--; + } + static void _gfxThreadsInit(void) { + uint32_t i; + char ** pout; + char ** pin; + size_t diff; + char * framebase; + saveloc tmpsaveloc[2]; + + // Create the main thread context + mainthread.cxt = maincxt; + + // Allocate a buffer to store our test data + pframeinfo = tmpsaveloc; + + // Get details of the stack frame from within a function + _gfxGetStackStateInFn(); + + // Get details of the stack frame outside the function + _gfxGetStackState(); + + /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */ + stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr; + pout = (char **)pframeinfo[0].cxt; + pin = (char **)pframeinfo[1].cxt; + diff = pframeinfo[0].localptr - pframeinfo[1].localptr; + framebase = pframeinfo[0].localptr; + jmpmask1 = jmpmask2 = 0; + for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { + if ((size_t)(*pout - *pin) == diff) { + if (i < 32) + jmpmask1 |= 1 << i; + else + jmpmask2 |= 1 << (i-32); + + if (stackdirup) { + if (framebase > *pout) + framebase = *pout; + } else { + if (framebase < *pout) + framebase = *pout; + } + } + } + stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr); + } + + #endif + + // Move the stack frame and relocate the context data + static void _gfxAdjustCxt(thread *t) { + char ** s; + char * nf; + int diff; + uint32_t i; + + // Copy the stack frame + #if AUTO_DETECT_STACKFRAME + if (STACK_DIR_UP) { // Stack grows up + nf = (char *)(t) + sizeof(thread) + sizeof(jmp_buf) + stackbase; + memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *)); + } else { // Stack grows down + nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); + memcpy(nf, &s, stackbase+sizeof(char *)); + } + #elif STACK_DIR_UP + // Stack grows up + nf = (char *)(t) + sizeof(thread) + sizeof(jmp_buf) + stackbase; + memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *)); + #else + // Stack grows down + nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); + memcpy(nf, &s, stackbase+sizeof(char *)); + #endif + + // Relocate the context data + s = (char **)(t->cxt); + diff = nf - (char *)&s; + + // Relocate the elements we know need to be relocated + for (i = MASK1; i ; i >>= 1, s++) { + if ((i & 1)) + *s += diff; + } + #ifdef MASK2 + s = (char **)(t->cxt)+32; + for (i = MASK2; i ; i >>= 1, s++) { + if ((i & 1)) + *s += diff; + } + #endif + } + static void _gfxXSwitch(thread *oldt, thread *newt, bool_t doBuildFrame) { + + // Save the old context + if (CXT_SAVE(oldt->cxt)) return; + + // Do we need to build a new context? + if (doBuildFrame) { + + // Save our existing context as a starting point for the new context + newt->cxt = newt+1; + if (CXT_SAVE(newt->cxt)) { + + // We are now running the new thread + + // We can't use any of the above function parameters here + // as we are on a different stack. + + // Run the users function. + gfxThreadExit(current->fn(current->param)); + + // We never get here as gfxThreadExit() never returns + } + + // Adjust the new context so the stack references are correct + _gfxAdjustCxt(newt); + } + + // Start the new context + CXT_RESTORE(newt->cxt, 1); + } + + #define _gfxTaskSwitch(oldt, newt) _gfxXSwitch(oldt, newt, FALSE) + #define _gfxStartThread(oldt, newt) _gfxXSwitch(oldt, newt, TRUE) + +#elif GFX_CPU == GFX_CPU_CORTEX_M0 || GFX_CPU == GFX_CPU_CORTEX_M1 + + // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 + // The context is saved at the current stack location and a pointer is maintained in the thread structure. + + #define _gfxThreadsInit() + + static __attribute__((pcs("aapcs"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { + __asm__ volatile ( "push {r4, r5, r6, r7, lr} \n\t" + "mov r4, r8 \n\t" + "mov r5, r9 \n\t" + "mov r6, r10 \n\t" + "mov r7, r11 \n\t" + "push {r4, r5, r6, r7} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + "pop {r4, r5, r6, r7} \n\t" + "mov r8, r4 \n\t" + "mov r9, r5 \n\t" + "mov r10, r6 \n\t" + "mov r11, r7 \n\t" + "pop {r4, r5, r6, r7, pc} \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + } + + static __attribute__((pcs("aapcs"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { + newt->cxt = (char *)newt + newt->size; + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" // save current context + "str sp, %[oldtcxt] \n\t" // save context pointer + "ldr sp, %[newtcxt] \n\t" // load new context pointer + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + + // Run the users function + gfxThreadExit(current->fn(current->param)); + } + +#elif GFX_CPU == GFX_CPU_CORTEX_M3 || GFX_CPU == GFX_CPU_CORTEX_M4 || GFX_CPU == GFX_CPU_CORTEX_M7 + + // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 + // The context is saved at the current stack location and a pointer is maintained in the thread structure. + + #if CORTEX_USE_FPU + #warning "GOS Threads: You have specified GFX_CPU=GFX_CPU_CORTX_M? with no hardware floating point support but CORTEX_USE_FPU is TRUE. Try using GFX_CPU_GFX_CPU_CORTEX_M?_FP instead" + #endif + + #define _gfxThreadsInit() + + static __attribute__((pcs("aapcs"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + "pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + } + + static __attribute__((pcs("aapcs"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { + newt->cxt = (char *)newt + newt->size; + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + + // Run the users function + gfxThreadExit(current->fn(current->param)); + } + +#elif GFX_CPU == GFX_CPU == GFX_CPU_CORTEX_M4_FP || GFX_CPU == GFX_CPU_CORTEX_M7_FP + + // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 and floating point + // The context is saved at the current stack location and a pointer is maintained in the thread structure. + + #if !CORTEX_USE_FPU + #warning "GOS Threads: You have specified GFX_CPU=GFX_CPU_CORTX_M?_FP with hardware floating point support but CORTEX_USE_FPU is FALSE. Try using GFX_CPU_GFX_CPU_CORTEX_M? instead" + #endif + + #define _gfxThreadsInit() + + static __attribute__((pcs("aapcs-vfp"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "vpush {s16-s31} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + "vpop {s16-s31} \n\t" + "pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + } + + static __attribute__((pcs("aapcs-vfp"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { + newt->cxt = (char *)newt + newt->size; + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "vpush {s16-s31} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + + // Run the users function + gfxThreadExit(current->fn(current->param)); + } + +#else + #error "GOS Threads: Unsupported Scheduler. Try setting GFX_CPU = GFX_CPU_UNKNOWN" +#endif + +static void Qinit(threadQ * q) { + q->head = q->tail = 0; +} + +static void Qadd(threadQ * q, thread *t) { + t->next = 0; + if (q->head) { + q->tail->next = t; + q->tail = t; + } else + q->head = q->tail = t; +} + +static thread *Qpop(threadQ * q) { + struct thread * t; + + if (!q->head) + return 0; + t = q->head; + q->head = t->next; + return t; +} + +void _gosThreadsInit(void) { + Qinit(&readyQ); + + mainthread.next = 0; + mainthread.size = sizeof(thread); + mainthread.flags = FLG_THD_MAIN; + mainthread.fn = 0; + mainthread.param = 0; + + _gfxThreadsInit(); + + current = &mainthread; +} + +gfxThreadHandle gfxThreadMe(void) { + return (gfxThreadHandle)current; +} + +// Check if there are dead processes to deallocate +static void cleanUpDeadThreads(void) { + thread *p; + + while ((p = Qpop(&deadQ))) + gfxFree(p); +} + +void gfxYield(void) { + thread *me; + + // Clean up zombies + cleanUpDeadThreads(); + + // Is there another thread to run? + if (!readyQ.head) + return; + + Qadd(&readyQ, me = current); + current = Qpop(&readyQ); + _gfxTaskSwitch(me, current); +} + +// This routine is not currently public - but it could be. +void gfxThreadExit(threadreturn_t ret) { + thread *me; + + // Save the results in case someone is waiting + me = current; + me->param = (void *)ret; + me->flags |= FLG_THD_DEAD; + + // Add us to the dead list if we need deallocation as we can't free ourselves. + // If someone is waiting on the thread they will do the cleanup. + if ((me->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) + Qadd(&deadQ, me); + + // Set the next thread. Exit if it was the last thread + if (!(current = Qpop(&readyQ))) + gfxExit(); + + // Switch to the new thread + _gfxTaskSwitch(me, current); + + // We never get back here as we didn't re-queue ourselves +} + +gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { + thread * t; + thread * me; + (void) prio; + + // Ensure we have a minimum stack size + if (stacksz < sizeof(thread)+64) { + stacksz = sizeof(thread)+64; + stackarea = 0; + } + + if (stackarea) { + t = (thread *)stackarea; + t->flags = 0; + } else { + t = (thread *)gfxAlloc(stacksz); + if (!t) + return 0; + t->flags = FLG_THD_ALLOC; + } + t->size = stacksz; + t->fn = fn; + t->param = param; + + // Add the current thread to the queue because we are starting a new thread. + me = current; + Qadd(&readyQ, me); + current = t; + + _gfxStartThread(me, t); + + // Return the new thread handle + return t; +} + +threadreturn_t gfxThreadWait(gfxThreadHandle th) { + thread * t; + + t = th; + if (t == current) + return -1; + + // Mark that we are waiting + t->flags |= FLG_THD_WAIT; + + // Wait for the thread to die + while(!(t->flags & FLG_THD_DEAD)) + gfxYield(); + + // Unmark + t->flags &= ~FLG_THD_WAIT; + + // Clean up resources if needed + if (t->flags & FLG_THD_ALLOC) + gfxFree(t); + + // Return the status left by the dead process + return (threadreturn_t)t->param; +} + +#endif /* GFX_USE_OS_RAW32 */ diff --git a/src/gos/gos_x_threads.h b/src/gos/gos_x_threads.h new file mode 100644 index 00000000..78c30ac4 --- /dev/null +++ b/src/gos/gos_x_threads.h @@ -0,0 +1,103 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This threading implementation supports most 32 bit processors with or without an + * underlying operating system. It uses cooperative multi-tasking. Be careful + * when writing device drivers not to disturb the assumptions this creates by performing + * call-backs from interrupt handlers to uGFX code unless you define the INTERRUPTS_OFF() + * and INTERRUPTS_ON() macros. + * It still requires some C runtime library support for the setjmp implementation... + * setjmp() and longjmp() - for threading + * memcpy() - for heap and threading + * + * You must also define the following routines in your own code so that timing functions will work... + * systemticks_t gfxSystemTicks(void); + * systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + */ +#ifndef _GOS_X_THREADS_H +#define _GOS_X_THREADS_H + +#if GOS_NEED_X_THREADS + +typedef uint32_t delaytime_t; +typedef uint32_t systemticks_t; +typedef short semcount_t; +typedef int threadreturn_t; +typedef int threadpriority_t; + +#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) +#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; + +#define TIME_IMMEDIATE 0 +#define TIME_INFINITE ((delaytime_t)-1) +#define MAX_SEMAPHORE_COUNT 0x7FFF +#define LOW_PRIORITY 0 +#define NORMAL_PRIORITY 1 +#define HIGH_PRIORITY 2 + +typedef struct { + semcount_t cnt; + semcount_t limit; +} gfxSem; + +typedef uint32_t gfxMutex; +typedef void * gfxThreadHandle; + +#ifdef __cplusplus +extern "C" { +#endif + + // Required timing functions - supplied by the user or the operating system + systemticks_t gfxSystemTicks(void); + systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + + // Sleep Functions + void gfxSleepMilliseconds(delaytime_t ms); + void gfxSleepMicroseconds(delaytime_t ms); + void gfxYield(void); + + // System Locking + void gfxSystemLock(void); + void gfxSystemUnlock(void); + + // Mutexes + void gfxMutexInit(gfxMutex *pmutex); + #define gfxMutexDestroy(pmutex) + void gfxMutexEnter(gfxMutex *pmutex); + void gfxMutexExit(gfxMutex *pmutex); + + // Semaphores + void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); + #define gfxSemDestroy(psem) + bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); + bool_t gfxSemWaitI(gfxSem *psem); + void gfxSemSignal(gfxSem *psem); + void gfxSemSignalI(gfxSem *psem); + + // Deprecated Semaphore functions (they still work here) + #define gfxSemCounter(psem) ((psem)->cnt) + #define gfxSemCounterI(psem) ((psem)->cnt) + + // Threads + gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); + #define gfxThreadClose(thread) + threadreturn_t gfxThreadWait(gfxThreadHandle thread); + gfxThreadHandle gfxThreadMe(void); + + /** The following is not part of the public ugfx API has some operating systems + * simply do not provide this capability. + * For RAW32 we need it anyway so we might as well declare it here. + */ + void gfxThreadExit(threadreturn_t ret); + +#ifdef __cplusplus +} +#endif + +#endif /* GOS_NEED_X_THREADS */ +#endif /* _GOS_X_THREADS_H */ |