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 */ | 
