STM32单片机项目实例:基于TouchGFX的智能手表设计(5)硬件驱动层程序设计

news2024/11/15 4:41:41

STM32单片机项目实例:基于TouchGFX的智能手表设计(5)硬件驱动层程序设计

目录

一、 概述

二、 新建工程与外设配置

三、 TouchGFX配置

四、 增加TouchGFX关键驱动


一、 概述

  本文内容主要进行工程新建,硬件外设的配置以及添加TouchGFX的屏幕、触摸NorFlash驱动,主要以流程为主。

二、  新建工程与外设配置

  新建工程,选择STM32U575RIT6,弹出的TrustZone选项,选择without TrustZone activated。

  Project Manager页面,设置工程名:0C-1_STM32U575_Hardware_Basic,选择工程路径,工具链选择MDK-ARM,设置堆栈大小0x1200,Code Generator页面,Copy only the…,Generate peripheral initialization as a pair…,

  Clock Configuration页面,系统时钟选择PLLCK输出,在HCLK框输入160后回车,设置系统时钟频率为160MHz。

    Pinout & Configuration 页面,System Core栏中的ICACHE进行设置。

  Pinout & Configuration 页面,System Core栏中的RCC进行设置,HSE与LSE设置位晶体谐振器。

  返回至Clock Configuration页面,HSE的外部输入频率设置为12。

  Pinout & Configuration 页面,Connectivity栏中设置SPI屏幕接口,I2C触摸接口,外部存储器的QSPI接口,UART调试端口,WiFi模组的通信口(UART)。首先是屏幕的SPI接口,设置全双工主机,按住ctrl,光标移动到PA1端口,点击左键拖拽SPI1_SCK至PA5,并将PA6/PA7右键Signal Pinning进行端口锁定。

  在SPI1的Parameter Settings栏中,设置Data Size:8,Prescaler(for Baud Rate):4。

  在GPIO Settings中将PA5、PA6、PA7的Maximum outut speed设置为Very Hight。

   返回到System Core栏中的GPIO,对显示屏的控制引脚以及按键、LED灯与蜂鸣器进行设置。设置PA4为超高速PP输出,修改用户标签为LCD_DCX;设置PB10为低速PP输出,初始电平为高,修改用户标签为LCD_BL;设置PA8为高速PP输出,修改用户标签为LCD_RST;设置PA12为下降沿触发,修改用户标签为USER_KEY;设置PA15为低速PP输出,修改用户标签为RUN-BEEP;设置PC13为低速PP输出,修改用户标签为BLUE_LED。

  对Connectivity栏中I2C1接口设置为I2C,按住ctrl,光标移动到PA3端口,点击左键拖拽I2C1_SDA至PB7,并将PB6/PB7右键Signal Pinning进行端口锁定。修改I2C Speed Mode为Fast Mode。

  返回到System Core栏中的GPIO,对触摸屏的控制引脚以进资源扩展板的外设行设置。设置PA0为双边沿触发,修改用户标签为EXT-FIVEKEY;设置PC6为低速PP输出,修改用户标签为EXT-FAN;设置PC6为低速PP输出,修改用户标签为EXT-MOTOR;设置PA11为低速PP输出,修改用户标签为TP_RST;设置PA12为下降沿触发,修改用户标签为USER_KEY;设置PB5为双边沿触发,修改用户标签为TP_INT。

    在NVIC中勾选EXTI Line0/ EXTI Line5全局中断使能。

  对Connectivity栏中OCTOSPI1接口设置模式为Quad SPI,Clock选择Port1 CLK,Chip Select选择Port1 NCS,Data[3:0]选择Port1 IO[7:0];设置Fifo Threshold为8,Device Size为24,Clock Prescaler为2,Sample Shiting为Half Cycle。右键将PC0、PC1、PC2、PC3、PA2、PA3固定。

  对Connectivity栏中USART1进行设置,mode设置Asynchronous,并锁定PA9与PA10。

  对Connectivity栏中UART5进行设置,mode设置Asynchronous,并锁定PD2与PC12。

  在NVIC Settings中勾选UART5全局中断使能。

  对Timers进行设置,TIM16/TIM17分别设置5ms与100ms的中断,用于任务的触发管理。

    在NVIC Settings中勾选TIM16全局中断使能。

  对Timers栏中的RTC进行设置

  对Analog栏中的ADC1进行设置,通道6、通道15、通道16设置为单端转换,开启内部的VBAT、VREF以及芯片内部温度测量。

  ADC参数设置一栏,设置时钟分频÷4,采样精度12位,Enable Regular Conversions进行使能,通道数量6,连续扫描模式使能,Rank1、Rank2、Rank3、Rank4、Rank5、Rank6分别对应通道6,通道15,通道16,内部温度,参考电压以及VBAT电压测量,采样时间设置为391.5Cycles。启动DMA循环模式。

  在System Core栏中对GPDMA进行设置,通道0与通道1设置位标准请求模式,通道0用于屏幕数据刷新,通道1用于外部存储器的读写。

  CH0的请求采用SPI1_TX,通道配置的优先级高,传输模式正常,传输方向内存到外设,源端地址自增使能,采用半字传输,目标地址自增禁止,半字传输,目标端口设置为1。

  CH1采用循环模式,端口选则Port1,请求采用ADC1,通道配置的优先级高,传输模式正常,传输方向外设到内存,源端地址自增禁用,采用半字传输,源端端口设置Port1;目标地址自增使能,半字传输,目标端口设置为Port1。

  在System Core栏中对NVIC的优先级进行设置。 

  点击右上角的GENERATR CODE,等待完成生成工程配置。

三、 TouchGFX配置

对Computing栏中的CRC进行设置。

  对Middleware and Software Packs中的X-CUBE-TOUCHGFX进行选择4.21.2版本。

  对Middleware and Software Packs中的X-CUBE-TOUCHGFX图形应用进行勾选。采用单缓冲,设置宽度320与高度240。

  点击右上角的GENERATR CODE,等待完成生成工程配置。生成配置后的代码,编译会报错,需要打开TouchGFX 4.21.2 Designer软件进行一次配置与GUI代码的生成(注意工程路径中不能包含非法字符,空格也不可以)。

  打开…\0C-2_STM32U575_Hardware_Basic\TouchGFX下的ApplicationTemplate.touchgfx.part,点击右下角的</>生成代码。

打开…\0C-2_STM32U575_Hardware_Basic\MDK-ARM下的0C-2_STM32U575_Hardware_Basic.uvprojx,点击全编译,编译0错误,1警告(可忽略该警告)。

四、 增加TouchGFX关键驱动

  

  将资料光盘中..\FS-STM32U575-WATCH(Release)\Drivers文件夹下的efsm-master、MAX30102_Maxim、Motion_Driver三个文件夹拷贝至新建工程目录..\0C-2_STM32U575_Hardware_Basic\Drivers下。

  将资料光盘中.. \FS-STM32U575-WATCH(Release)\Core\Src文件夹下的bsp_ap3216c.c、bsp_esp8266.c、bsp_ft6336.c、bsp_ili9341_4line.c、bsp_ospi_w25q128.c、bsp_sht20.c、user_app.c拷贝至新建工程目录..\0C-2_STM32U575_Hardware_Basic\Core\Src下。

  将资料光盘中.. \FS-STM32U575-WATCH(Release)\Core\Irc文件夹下的bsp_ap3216c.h、bsp_esp8266.h、bsp_ft6336.h、bsp_ili9341_4line.h、bsp_ospi_w25q128.h、bsp_sht20.h、user_app.h拷贝至新建工程目录..\0C-2_STM32U575_Hardware_Basic\Core\Irc下。

  打开…\0C-2_STM32U575_Hardware_Basic\MDK-ARM下的0C-2_STM32U575_Hardware_Basic.uvprojx,点击菜单栏的工程管理快捷图标,在Groups中增加motion、efsm-master、MAX30102_Maxim组,并在Files族中添加相应的.c文件。

  在Application/User/Core中增加bsp_ap3216c.c、bsp_esp8266.c、bsp_ft6336.c、bsp_ili9341_4line.c、bsp_ospi_w25q128.c、bsp_sht20.c、user_app.c文件。

  点击菜单栏的魔术棒图标,在C/C++(AC6),增加motion、efsm-master、MAX30102_Maxim的头文件路径。

  打开main.c文件,在Private includes的BEGIN与END之间增加以下代码,如出现乱码,调整编码格式为UTF-8。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "user_app.h"		//用户应用程序

/* USER CODE END Includes */

在Private define的BEGIN与END之间增加以下代码,用于ADC的多通达数据采集。

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define	ADC_CONVERTED_DATA_BUFFER_SIZE	6	
/* USER CODE END PD */

在Private variables的BEGIN与END之间增加以下代码,用于 任务标志组与ADC多通道采集。

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t gChatCount = 10;  //全局按键按压计数
extern gTask_MarkEN gTaskEnMark;  //系统任务使能标识
static uint8_t gTaskIndex = 0x00;  //系统任务索引变量
extern volatile uint8_t TcpClosedFlag;	
ADC_ValTypeDef gStruADC={0,0,0,0,0,0}; //A/D通道实时采集的数据
/* USER CODE END PV */
在Private function的BEGIN与END之间增加以下代码,用于touchgfx接口声明与wifi模组数据接收。
/* USER CODE BEGIN PFP */
extern void touchgfx_signalVSynTimer(void);
extern gTask_BitDef gTaskStateBit;  //任务执行过程中使用到的标志位
volatile uint8_t gRX3_BufF[1];	//串口3-wifi模组接收到的数据
/* USER CODE END PFP */

在Private user code的BEGIN与END之间增加半主机模式的设置与外部存储器测试。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**************************关闭标准库下的半主机模式****************************/
__ASM (".global __use_no_semihosting");	//AC6编译器
//标准库需要的支持函数
struct FILE 
{
  int handle; 
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式  
void _sys_exit(int x) 
{ 
  x = x; 
}
void _ttywrch(int ch)
{
  ch = ch;
}
//printf实现重定向
int fputc(int ch, FILE *f)	
{
	uint8_t temp[1] = {ch};
	HAL_UART_Transmit(&huart1, temp, 1, 2);
	return ch;
}
/******************************************************************************/

/*******************************************************************************
*	函 数 名: OSPI_W25Qxx_mmap
*	入口参数: 无
*	返 回 值: 无
*	函数功能: 设置为内存映射模式
*	说    明: 无	
*******************************************************************************/
void OSPI_W25Qxx_mmap(void)		//Flash读写测试
{
	int32_t OSPI_Status ; 		 //检测标志位
	//
	OSPI_Status = OSPI_W25Qxx_MemoryMappedMode(); //配置OSPI为内存映射模式
	if( OSPI_Status == OSPI_W25Qxx_OK )
	{
		printf ("\r\n内存映射模式设置成功>>>>\r\n");	
	}
	else
	{
		printf ("\r\n内存映射模式设置失败>>>>\r\n");
		Error_Handler();
	}	
}
/* USER CODE END 0 */
在main函数中增加相关外设的初始化配置。
  /* USER CODE BEGIN 2 */
//ESP8266初始化,HAL库使用USART3
ESP8266_Init(&huart5,(uint8_t *)gRX3_BufF,115200);
ap3216c_init();	//环境光传感器初始化
  ILI9341_Init();	//显示屏初始化
  FT6336_init();	//触摸屏初始化
mpu_init_dmp();	//mpu6050 dmp初始化
System_Time_init();	
  //NOR Flash初始化
  OSPI_W25Qxx_Init();	//初始化W25Q128
OSPI_W25Qxx_mmap();	//设置为内存映射模式
HAL_PWREx_EnableVddA();
HAL_PWREx_EnableVddIO2();

//清空任务列表
for(gTaskIndex = 0;gTaskIndex < OS_TASKLISTCNT;gTaskIndex++)	g_OSTsakList[gTaskIndex]=NULL;
	
//读取ADC值
if (HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&gStruADC,ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK)	{Error_Handler();}
  /* USER CODE END 2 */	

在while (1)函数前增加任务调度的定时器使能。

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_TIM_Base_Start_IT(&htim16);//开启定时器16开启,系统任务调度开始
  HAL_TIM_Base_Start_IT(&htim17);//开启定时器17开启,设备控制任务开始  

在while (1)函数中增加任务调度管理代码。

while (1)
  {
    /* USER CODE END WHILE */

  MX_TouchGFX_Process();
    /* USER CODE BEGIN 3 */
		//执行任务列表中的的任务
		for(gTaskIndex = 0;gTaskIndex < OS_TASKLISTCNT;gTaskIndex++)
		{
		if((*g_OSTsakList[gTaskIndex]) != NULL)
			{
				g_OSTsakList[gTaskIndex]();
				g_OSTsakList[gTaskIndex] = NULL;  
			}
		}
  }
  /* USER CODE END 3 */

在USER CODE BEGIN 4位置增加中断回调函数,用于任务的调度、串口数据的接收处理。

/* USER CODE BEGIN 4 */
//定时器16/17的任务分配
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static uint8_t p_Time16Cnt = 0,p_Time17Cnt = 0;
	/***************************************************************************************/
	//定时器16进行5ms任务中断
	if (htim->Instance == htim16.Instance) 
	{
		p_Time16Cnt++;
		//
		if(!(p_Time16Cnt % 4))  //20ms(50Hz)进行触发刷新
		{
			touchgfx_signalVSynTimer();  //touchgfx用户接口
		}
		//五项按键读取
		if(!(p_Time16Cnt % 20))  //100ms进行一次窗口更新
		{
			if(gTaskStateBit.TouchPress == 0) 
			{
				g_OSTsakList[eUPDATE_FIVEKEY] = Update_FiveKey_Value; 	//更新五向键数据				
			}
		}
		//1000ms运行一次,系统运行指示灯
		if(!(p_Time16Cnt % 200))  
		{
			p_Time16Cnt = 0; 
			HAL_GPIO_TogglePin(BLUE_LED_GPIO_Port,BLUE_LED_Pin);
		}
	}
	/***************************************************************************************/
	//定时器17进行100ms任务中断
	if (htim->Instance == htim17.Instance) 
	{
p_Time17Cnt++;
		//周期为200ms任务
		if(!(p_Time17Cnt % 2))  //200ms进行一次下列代码
		{
			if((gTaskEnMark.UPDATE_DIAL_EN || gTaskEnMark.UPDATE_SIX_AXIS_EN) && (gTaskStateBit.TouchPress == 0)) 
			{
				g_OSTsakList[eUPDATE_SIX_AXIS] = Update_EulerAngle; 	//欧拉角更新
			}
		}
		//周期为300ms任务
		if(!(p_Time17Cnt % 3))  //300ms进行一次下列代码
		{
			if(gTaskEnMark.UPDATE_WIFI_RSSI_EN) g_OSTsakList[eUPDATE_WIFI_RSSI] = ESP8266_RSSI_Task; 	//获取wifi连接的RSSI值			
		}	
		//周期为500ms任务
		if(!(p_Time17Cnt % 5))  //500ms进行一次下列代码
		{
			if(gTaskEnMark.UPDATE_CHIPPAGE) g_OSTsakList[eUPDATE_CHIPINFO] = Update_ChipInfo; 	//系统信息更新
		}
		//周期为1000ms任务
		if(!(p_Time17Cnt % 10))  //1s进行一次下列代码
		{
			if(gTaskEnMark.UPDATE_DIAL_EN && (gTaskStateBit.TouchPress == 0))
			{
				g_OSTsakList[eUPDATE_TIME] = Update_System_Time; 	//系统时间更新
			}				
		}
		//周期为2000ms任务	
		if(!(p_Time17Cnt % 20))  //2s进行一次下列代码
		{
			if((gTaskEnMark.UPDATE_DIAL_EN || gTaskEnMark.UPDATE_INFOPAGE) && (gTaskStateBit.TouchPress == 0))
			{
				g_OSTsakList[eUPDATE_DIAL_INFO] = Update_DialInfo;	//更新电压、电流、温湿度、光照度
			}		
		}	
		//周期为3000ms任务
		if(!(p_Time17Cnt % 30))  //3s进行一次下列代码
		{
			//心率任务会阻塞主程序
			if(gTaskEnMark.UPDATE_HEALTHPAGE) 
			{
	g_OSTsakList[eUPDATE_HEART_RATE] = Update_HeartRateInfo; 	//获取健康信息
			}		
		}
		//周期为10000ms任务
		if(!(p_Time17Cnt % 100))  //10s进行一次下列代码
		{
			p_Time17Cnt = 0; 
		}
	}
	/***************************************************************************************/
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);
}
/**
  * @brief  EXTI line rising detection callback.
  * @param  GPIO_Pin: Specifies the port pin connected to corresponding EXTI line.
  * @retval None
  */
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
	UNUSED(GPIO_Pin);
	//触摸按下事件
	if((!HAL_GPIO_ReadPin(TP_INT_GPIO_Port,TP_INT_Pin)) && (GPIO_Pin == TP_INT_Pin))
	{
		gTaskStateBit.TouchPress = 1;
	}
	if(!HAL_GPIO_ReadPin(USER_KEY_GPIO_Port,USER_KEY_Pin))
	{
		gChatCount = gChatCount + 10;
		if(gChatCount>=100) gChatCount=10;
	}
}
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
	UNUSED(GPIO_Pin);
	//触摸释放事件
	if(HAL_GPIO_ReadPin(TP_INT_GPIO_Port,TP_INT_Pin) && gTaskStateBit.TouchPress)
	{
		FT6336_irq_fuc();	//触摸中断产生
		gTaskStateBit.TouchPress = 0;	//清除触摸标志
}
	//五向按键按下
	if(HAL_GPIO_ReadPin(EXT_FIVEKEY_GPIO_Port,EXT_FIVEKEY_Pin) && (GPIO_Pin == EXT_FIVEKEY_Pin) && (gTaskStateBit.TouchPress == 0))
	{
		gTaskStateBit.FiveKeyPress = 1; 
	}
}
/**
  * @brief  Conversion complete callback in non blocking mode 
  * @param  hadc: ADC handle
  * @note   This example shows a simple way to report end of conversion
  *         and get conversion result. You can add your own implementation.
  * @retval None
  */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
  //更新DMA传输状态标志
  gTaskStateBit.ADCC = 1;  
}
/**
  * @brief  Rx Transfer completed callback.
  * @param  huart UART handle.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//USART3接收数据
	if (huart->Instance == UART5) 
	{
		if(ESP8266_Fram_Record_Struct .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) ) 	//接收到的数据存储至buffer
		{
			//留最后一位做结束位
			ESP8266_Fram_Record_Struct .Data_RX_BUF[ ESP8266_Fram_Record_Struct .InfBit .FramLength ++ ]  = gRX3_BufF[0];  
			//UART3开启下一次接收
			HAL_UART_Receive_IT(&huart5,(uint8_t *)&gRX3_BufF, 1);//接收一个字节
		}  
	}

}
/**
  * @brief  UART Abort Receive Complete callback.
  * @param  huart UART handle.
 * @retval None
  */
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)
{
	//USART3帧传输完成,产生空闲
	if (huart->Instance == UART5)
	{
		ESP8266_Fram_Record_Struct .InfBit .FramFinishFlag = 1;	//帧传输完成标志
		TcpClosedFlag = strstr ( ESP8266_Fram_Record_Struct .Data_RX_BUF, "CLOSED\r\n" ) ? 1 : 0;	//判断连接
		//UART3开启下一次接收
		HAL_UART_Receive_IT(&huart5,(uint8_t *)&gRX3_BufF, 1);//接收一个字节
	} 	
}
/* USER CODE END 4 */
在stm32u5xx_it.c文件中,UART5_IRQHandler中断函数中增加空闲中断判断。
void UART5_IRQHandler(void)
{
  /* USER CODE BEGIN UART5_IRQn 0 */

  /* USER CODE END UART5_IRQn 0 */
  HAL_UART_IRQHandler(&huart5);
  /* USER CODE BEGIN UART5_IRQn 1 */
	if (__HAL_UART_GET_FLAG(&huart5, UART_FLAG_IDLE) != RESET)	
	{	
		__HAL_UART_CLEAR_IDLEFLAG(&huart5);	
		HAL_UART_AbortReceive_IT(&huart5);	
	}
  /* USER CODE END UART5_IRQn 1 */
}
在TouchGFXHAL.cpp文件中增加头文件包含。
/* USER CODE BEGIN TouchGFXHAL.cpp */
#include <touchgfx/hal/OSWrappers.hpp>
//用户外设驱动头文件
#include "spi.h"
#include "stm32u5xx_hal.h"
//
extern "C" void ILI9341_SetArea(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);	//设置显示区域
extern "C"  void ILI9341_WriteRAM_Prepare(void);
//
extern SPI_HandleTypeDef hspi1;
extern DMA_HandleTypeDef handle_GPDMA1_Channel0;
//

 增加屏幕的DMA刷新函数

/**
* Sets the frame buffer address used by the TFT controller.
 *
 * @param [in] address New frame buffer address.
 */
void TouchGFXHAL::setTFTFrameBuffer(uint16_t* address)
{
    // Calling parent implementation of setTFTFrameBuffer(uint16_t* address).
    //
    // To overwrite the generated implementation, omit call to parent function
    // and implemented needed functionality here.

    TouchGFXGeneratedHAL::setTFTFrameBuffer(address);
}
static void USER_SPI_Transmit_DMA(const uint16_t *pData, uint16_t pSize)
{
		//Set the transaction information
		hspi1.State       = HAL_SPI_STATE_READY;
		hspi1.ErrorCode   = HAL_SPI_ERROR_NONE;

		//Init field not used in handle to zero
		hspi1.RxISR       = NULL;
		hspi1.TxISR       = NULL;

		//Configure communication direction : 1Line
		SPI_2LINES_TX(&hspi1);

		// Packing mode management is enabled by the DMA settings
		IS_SPI_FULL_INSTANCE(hspi1.Instance);

		//Clear TXDMAEN bit
		CLEAR_BIT(hspi1.Instance->CFG1, SPI_CFG1_TXDMAEN);

		//Update the DMA channel state
		handle_GPDMA1_Channel0.State = HAL_DMA_STATE_BUSY;
		//Update the DMA channel error code
		handle_GPDMA1_Channel0.ErrorCode = HAL_DMA_ERROR_NONE;

		//Configure the source address, destination address, the data size and clear flags
		MODIFY_REG(handle_GPDMA1_Channel0.Instance->CBR1, DMA_CBR1_BNDT, ((pSize) & DMA_CBR1_BNDT));

		//Clear all interrupt flags
		__HAL_DMA_CLEAR_FLAG(&handle_GPDMA1_Channel0, DMA_FLAG_TC | DMA_FLAG_HT | DMA_FLAG_DTE | DMA_FLAG_ULE | DMA_FLAG_USE | DMA_FLAG_SUSP |
												 DMA_FLAG_TO);
	//Configure DMA channel source address
		handle_GPDMA1_Channel0.Instance->CSAR = (uint32_t)pData;

		//Configure DMA channel destination address
		handle_GPDMA1_Channel0.Instance->CDAR = (uint32_t)&hspi1.Instance->TXDR;

		//Enable common interrupts: Transfer Complete and Transfer Errors ITs
		__HAL_DMA_ENABLE_IT(&handle_GPDMA1_Channel0, (DMA_IT_TC | DMA_IT_DTE | DMA_IT_ULE | DMA_IT_USE | DMA_IT_TO));

		//If Half Transfer complete callback is set, enable the corresponding IT
		__HAL_DMA_ENABLE_IT(&handle_GPDMA1_Channel0, DMA_IT_HT);

		//Enable DMA channel
		__HAL_DMA_ENABLE(&handle_GPDMA1_Channel0);

		//Set the number of data at current transfer
		MODIFY_REG(hspi1.Instance->CR2, SPI_CR2_TSIZE, (pSize));

		//Enable Tx DMA Request
		SET_BIT(hspi1.Instance->CFG1, SPI_CFG1_TXDMAEN);

		//Enable the SPI Error Interrupt Bit
		__HAL_SPI_ENABLE_IT(&hspi1, (SPI_IT_UDR | SPI_IT_FRE | SPI_IT_MODF));

		//Enable SPI peripheral
		__HAL_SPI_ENABLE(&hspi1);
		//
		if (((hspi1.Instance->AUTOCR & SPI_AUTOCR_TRIGEN) == 0U) && (hspi1.Init.Mode == SPI_MODE_MASTER))
		{
			/* Master transfer start */
			SET_BIT(hspi1.Instance->CR1, SPI_CR1_CSTART);
		}	
}

在flushFrameBuffer函数中增加屏幕刷新功能。

/**
 * This function is called whenever the framework has performed a partial draw.
 *
 * @param rect The area of the screen that has been drawn, expressed in absolute coordinates.
 *
 * @see flushFrameBuffer().
 */
static uint16_t flushAreaBuf[31745];	//62KB区域性刷新缓冲区
void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
    // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
    //
    // To overwrite the generated implementation, omit call to parent function
    // and implemented needed functionality here.
    // Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
    // be called to notify the touchgfx framework that flush has been performed.
    // To calculate he start adress of rect,
    // use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
    // defined in TouchGFXGeneratedHAL.cpp
		/*************************************屏幕刷新开始*************************************/
		__IO uint16_t *pixels;  //帧缓冲地址
		__IO uint16_t pheight = 0,pWidth = 0,pBuffCnt = 0;
		//保存长度
		__IO uint32_t pTotalPixel = rect.width * rect.height * 2;
		__IO uint32_t pFull = pTotalPixel / 63488;	//62KB取整
		__IO uint32_t pRemain = pTotalPixel % 63488;	//62KB取余
		//设置显示区域
		ILI9341_SetArea(rect.x, rect.y,rect.x+rect.width-1, rect.y+rect.height-1);
		ILI9341_WriteRAM_Prepare();		//开始写入GRAM	
	
		//设置SPI数据格式为16位,缓冲区数据采用小端模式,ILI9341数据传输高字节在前
		hspi1.Instance->CFG1 &= (~0x1F);
		hspi1.Instance->CFG1 |= SPI_DATASIZE_16BIT;		
		//
		if((rect.width == 320) && (rect.height ==240))  //采用横屏,整个屏幕刷新
		{
			//获取像素点地址
			pixels = getClientFrameBuffer() + rect.x + (rect.y) * HAL::DISPLAY_WIDTH;	
			//传输长度为62KB的像素点
			for (pBuffCnt = 0; pBuffCnt < pFull; pBuffCnt++)
			{
				//启动DMA传输
				USER_SPI_Transmit_DMA((uint16_t *)pixels,63488);	//DMA单次传输最长0xFFFF
				//
				pixels = pixels + 31744;  //地址偏移
				//等待DMA传输完成
				while(HAL_DMA_GetState(&handle_GPDMA1_Channel0) != HAL_DMA_STATE_READY);	
				//适当延时
				HAL_Delay(0);
//阻塞模式下,终止正在的传输
				HAL_SPI_Abort(&hspi1);
			}
			//启动DMA传输
			USER_SPI_Transmit_DMA((uint16_t *)pixels,pRemain);	//剩余数据传输
			//等待DMA传输完成
			while(HAL_DMA_GetState(&handle_GPDMA1_Channel0) != HAL_DMA_STATE_READY);	
			//适当延时
			HAL_Delay(0);
			//阻塞模式下,终止正在的传输
			HAL_SPI_Abort(&hspi1);
		}
		else	//屏幕区域刷新
		{
			for (pheight = 0; pheight < rect.height; pheight++)
			{
				//缓冲区数据采用小端模式,ILI9341数据传输高字节在前
				pixels = getClientFrameBuffer() + rect.x + (pheight + rect.y) * HAL::DISPLAY_WIDTH;
				//读取限度点至缓冲区
				for (pWidth = 0; pWidth < rect.width; pWidth++)
				{
					flushAreaBuf[pBuffCnt++] = *pixels;	//读取像素点
					pixels++;	//调整偏移地址
					if(pBuffCnt >= 31744)	//缓冲区数据满,传输数据至屏幕
					{
						//启动DMA传输
						USER_SPI_Transmit_DMA((uint16_t *)flushAreaBuf,63488);
						//等待DMA传输完成
						while(HAL_DMA_GetState(&handle_GPDMA1_Channel0) != HAL_DMA_STATE_READY);	
						//适当延时
						HAL_Delay(0);
						//阻塞模式下,终止正在的传输
						HAL_SPI_Abort(&hspi1);
						//复位计数值
						pBuffCnt =0;
					}
				}
			}
			//启动DMA传输
			USER_SPI_Transmit_DMA((uint16_t *)flushAreaBuf,pBuffCnt * 2);
			//等待DMA传输完成
			while(HAL_DMA_GetState(&handle_GPDMA1_Channel0) != HAL_DMA_STATE_READY);
//适当延时
			HAL_Delay(0);
			//阻塞模式下,终止正在的传输
			HAL_SPI_Abort(&hspi1);
		}
		//设置SPI数据格式为8位
		hspi1.Instance->CFG1 &= (~0x1F);
		hspi1.Instance->CFG1 |= SPI_DATASIZE_8BIT;	
		/*************************************屏幕刷新完成*************************************/
		//
    TouchGFXGeneratedHAL::flushFrameBuffer(rect);
}
	

增加touchGFX的同步刷新函数。

bool TouchGFXHAL::beginFrame()
{
    return TouchGFXGeneratedHAL::beginFrame();
}

void TouchGFXHAL::endFrame()
{
    TouchGFXGeneratedHAL::endFrame();
}

extern "C"
void touchgfx_signalVSynTimer(void)
{
    /* VSync has occurred, increment TouchGFX engine vsync counter */
    HAL::getInstance()->vSync();

    /* VSync has occurred, signal TouchGFX engine */
    OSWrappers::signalVSync();
}
/* USER CODE END TouchGFXHAL.cpp */

在STM32TouchController.cpp函数中增加触摸屏驱动接口。

void STM32TouchController::init()
{
    /**
     * Initialize touch controller and driver
     *
     */
}

extern "C"
{
	#include "bsp_ft6336.h"
}
//
extern "C" FT6336_TouchPointType tp;
extern volatile uint32_t ft6336_on_touch_count;
//
bool STM32TouchController::sampleTouch(int32_t& x, int32_t& y)
{
    /**
     * By default sampleTouch returns false,
     * return true if a touch has been detected, otherwise false.
     *
     * Coordinates are passed to the caller by reference by x and y.
     *
     * This function is called by the TouchGFX framework.
     * By default sampleTouch is called every tick, this can be adjusted by HAL::setTouchSampleRate(int8_t);
     *
     */
		uint16_t xDiff = 0,yDiff = 0;	
		static uint16_t pI_Touch_X = 0, pI_Touch_Y = 0;
		//触摸中断产生
		if (ft6336_on_touch_count) // irq done
		{
			uint8_t id1 = FT6336_read_touch1_id(); // id1 = 0 or 1
			tp.tp[id1].status = (tp.tp[id1].status == release) ? touch : stream;
			tp.tp[id1].x = FT6336_read_touch1_x();
			tp.tp[id1].y = FT6336_read_touch1_y();
			tp.tp[~id1 & 0x01].status = release;
			//更新数据至TouchGFX
			if (tp.tp[0].status == 1)	//最多支持两个触点
			{ 
				xDiff = tp.tp[0].x > pI_Touch_X ? (tp.tp[0].x - pI_Touch_X): (pI_Touch_X - tp.tp[0].x);
				yDiff = tp.tp[0].y > pI_Touch_Y ? (tp.tp[0].y - pI_Touch_Y): (pI_Touch_Y - tp.tp[0].y);
				//判断阈值
				if ((xDiff + yDiff) > 5)
				{
					pI_Touch_X = tp.tp[0].x;
					pI_Touch_Y = tp.tp[0].y;
				}
				//更新触摸,横屏触摸坐标变换
				x = pI_Touch_Y;
				y = 240 - pI_Touch_X;
			}
				ft6336_on_touch_count = 0;
			//
			return true;
		}
		//
    return false;
}

点击菜单栏的魔术棒,在Linker栏中设置分散加载文件。

在.sct文件中增加外部存储器的加载描述。

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00200000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00200000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x000C0000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

LR_EROM1 0x90000000 0x1000000  {    ; load region size_region
  ER_EROM1 0x90000000 0x1000000  {  ; load address = execution address
		*.o (ExtFlashSection)
	}
}

修改完成后,保存文件。重新打开设置栏,查看注意是否去掉3中的勾选。。。。

点击菜单栏的魔术棒,在Debug栏中设置加载算法文件,算法加载文件的方法可以查看《STM32U5核心板NOR-Flash烧写算法的使用v1.0》。

点击全编译工程,至此底层驱动文件的添加完成。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1313133.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《PySpark大数据分析实战》-10.独立集群模式的代码运行

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

03. 医院设置_后端

1、Swagger2 测试工具 编写和维护接口文档是每个程序员的职责&#xff0c;根据Swagger2可以快速帮助我们编写最新的API接口文档&#xff0c;再也不用担心开会前仍忙于整理各种资料了&#xff0c;间接提升了团队开发的沟通效率。 swagger通过注解表明该接口会生成文档&#xf…

CSC公派研究生项目|电气工程在读博士谈丹麦奥尔堡大学联培体会

2023年已近兔尾&#xff0c;很多人已经开始新一年的规划&#xff0c;对于国内在读博士而言&#xff0c;申请国家留学基金委&#xff08;CSC&#xff09;公派研究生项目也开始列入议事日程&#xff0c;然而&#xff0c;如何申请&#xff1f;在国外学习收获如何&#xff1f;本篇知…

IDEA小技巧

目录 1. IDEA自动添加注释 创建类的时候自动添加注释 创建函数、方法的注释 1. IDEA自动添加注释 参考文档&#xff1a;idea java 自动添加文件注释 idea新建类自动注释_mob6454cc73c728的技术博客_51CTO博客 【操作工具】IDEA创建类及已有类添加注释-详细操作_idea设置创建…

【状态机FSM 序列检测 饮料机_2023.12.1】

同步状态机 概念 同步状态机&#xff08;同一脉冲边沿触发&#xff09;&#xff1a;有限个离散状态及某状之间的转移 异步状态机无法综合 分类 Moore状态机 只和状态有关&#xff0c;与输入无关 Mealy状态机 和状态和输入都有关 Mealy型比Moore型少一个状态 结构 由状态寄…

AI日报:OpenAI向新用户重新开放ChatGPT Plus订阅

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 文章目录 总览Chatgptplus重新开放订阅#暂停原因功能 OpenAI的1000万美元安全人工智能拨款拨款初衷学术捐赠 总览 ChatGPT Plus再次向新用户开放&#xff0c;但目前每三小时限制发送40条消息。 OpenAI还宣布拨款1000万美元…

喜报!Coremail荣获2023信创“大比武”优秀生态融合奖

近期&#xff0c;2023信创“大比武”金融业务创新应用赛道&#xff08;简称金融赛道&#xff09;活动正式落下帷幕。经过赛程的层层考核&#xff0c;中泰证券股份有限公司&#xff08;简称“中泰证券”&#xff09;与Coremail联合组成的“中泰证券CACTER邮件安全保卫队”最终在…

Linux篇:信号

一、信号的概念&#xff1a; ①进程必须识别能够处理信号&#xff0c;信号没有产生&#xff0c;也要具备处理信号的能力---信号的处理能力属于进程内置功能的一部分 ②进程即便是没有收到信号&#xff0c;也能知道哪些信号该怎么处理。 ③当进程真的受到了一个具体的信号的时候…

2021实战面试

1、Rem , em , px , % , vw 之间的区别 PX: px像素&#xff08;Pixel&#xff09;。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。 em: 1,子元素字体大小的em是相对于父元素字体大小 2,元素的width/height/padding/margin用em的话是相对于该元素的font-size rem:1rem是…

【回眸】Tessy 单元测试软件使用指南(三)怎么打桩和指针测试

目录 前言 Tessy 如何进行打桩操作 普通桩 高级桩 手写桩 Tessy单元测试之指针相关测试注意事项 有类型的指针&#xff08;非函数指针&#xff09;&#xff1a; 有类型的函数指针&#xff1a; void 类型的指针&#xff1a; 结语 前言 进行单元测试之后&#xff0c;但凡…

免费且强大卸载软件工具-Geek Uninstaller

Geek Uninstaller是一款用于Windows操作系统的免费卸载软件。它提供了一种比Windows内置卸载工具更彻底的卸载程序的方法。界面简单没有广告&#xff0c;操作也十分的简单。 特点 完全的程序卸载&#xff1a;Geek Uninstaller 被设计为彻底卸载程序&#xff0c;包括删除剩余…

YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定义模型

💡该教程为改进YOLO专栏,属于《芒果书》📚系列,包含大量的原创改进方式🚀 💡🚀🚀🚀内含改进源代码 按步骤操作运行改进后的代码即可💡更方便的统计更多实验数据,方便写作 YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定…

springboot发送邮件,内容使用thymeleaf模板引擎排版

springboot发送邮件,内容使用thymeleaf模板引擎排版 1、导入jar包2、yml设置3、收件人以及收件信息设置4、发邮件service5、模版页面6、controller 1、导入jar包 <!--发送邮件--><dependency><groupId>org.springframework.boot</groupId><artifac…

使用Axure RP结合内网穿透工具制作本地静态web页面并实现公网访问

作者简介&#xff1a; 懒大王敲代码&#xff0c;正在学习嵌入式方向有关课程stm32&#xff0c;网络编程&#xff0c;数据结构C/C等 今天给大家讲解使用Axure RP结合内网穿透工具制作本地静态web页面并实现公网访问&#xff0c;希望大家能觉得实用&#xff01; 欢迎大家点赞 &am…

订单系统设计-状态机

1. 状态机 1.1 状态机简介 状态机是有限状态自动机的简称&#xff0c;是现实事物运行规则抽象而成的一个数学模型。 有限状态机一般都有以下特点&#xff1a; 可以用状态来描述事物&#xff0c;并且任一时刻&#xff0c;事物总是处于一种状态&#xff1b;事物拥有的状态总数…

线程安全集合类

文章目录 1. ConcurrentHashMap2. LinkedBlockingQueue 阻塞队列3. ConcurrentLinkedQueue4. CopyOnWriteArrayList JDK1.7 hashmap采用数组加链表头插的方式&#xff0c;在扩容时会出现循环死链问题&#xff0c;A->B->C扩容后C->B->A AB BA出现循环死链。 1. Conc…

Dockerfile的介绍和使用

什么是dockerfile? Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。 docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerf…

【Monitor, Maintenance Operation, Script code/prgramme】

Summary of M,M&O,Program JD) Monitor & M&O Symbio信必优) Job chance/opportunities on Dec 12th, 20231.1) Content 招聘JD job description:1.2) suggestions from Ms Liang/Winnie on Wechat app1.3) Java微服务是什么&#xff1f;1.3.1) [URL Java 微服务](…

yarn系统架构与安装

1.1 YARN系统架构 YARN的基本思想是将资源管理和作业调度/监视功能划分为单独的守护进程。其思想是拥有一个全局ResourceManager (RM)&#xff0c;以及每个应用程序拥有一个ApplicationMaster (AM)。应用程序可以是单个作业&#xff0c;也可以是一组作业。 一个ResourceManage…

数据结构与算法之美学习笔记:36 | AC自动机:如何用多模式串匹配实现敏感词过滤功能?

目录 前言基于单模式串和 Trie 树实现的敏感词过滤经典的多模式串匹配算法&#xff1a;AC 自动机解答开篇内容小结 前言 本节课程思维导图&#xff1a; 很多支持用户发表文本内容的网站&#xff0c;比如 BBS&#xff0c;大都会有敏感词过滤功能&#xff0c;用来过滤掉用户输入…