最近在一块新板子移植正点原子触摸屏驱动,但板子不带24c02,每次运行都需要重新校准屏幕,正好板子自带了一个w25q16,用来代替储存校准数据。
移植好正点原子的屏幕驱动确定无误可以使用后,添加以下代码:
这是w25q16的头文件指令集,如果是其他型号,只需要修改对应的指令集即可,W25Q_SPI、W25Q_CS_Port、W25Q_CS_Pin修改为自己对应的。
#ifndef _W25Q16_H_
#define _W25Q16_H_
#include "spi.h"
#define W25Q_SPI hspi1 /* <句柄重命名> */
#define W25Q_CS_Port GPIOB /* <片选引脚NSS> */
#define W25Q_CS_Pin GPIO_PIN_0
#define W25Q_WRITE_ENABLE 0x06 /* <写使能> */
#define W25Q_WRITE_DISABLE 0x04 /* <写禁止> */
#define W25Q_READ_DATA 0x03 /* <读数据> */
#define W25Q_READ_STATA_REG1 0x05 /* <读状态寄存器1,紧跟着的字节就是当前状态> */
#define W25Q_READ_STATA_REG2 0x35 /* <读状态寄存器2,紧跟着的字节就是当前状态> */
#define W25Q_WRITE_STATA_REG 0x01 /* <写状态寄存器,写入两个字节,分别到寄存器1,和寄存器2> */
#define W25Q_Page_Program 0x02 /* <页编程,先跟3个地址字节,再跟一个数据字节> */
#define W25Q_Block_Erase 0xD8 /* <块擦除64k,三个地址字节> */
#define W25Q_Sector_Erase 0x20 /* <扇区擦除,跟三个地址> */
#define W25Q_Full_Erase 0xC7 /* <全片擦除> */
#define W25Q_Susp_Erase 0x75 /* <暂停擦除> */
#define W25Q_Rest_Erase 0x7A /* <恢复擦除> */
#define W25Q_PowDow_Mode 0xB9 /* <掉电模式> */
#define W25Q_HPer_Mode 0xA3 /* <高性能模式> */
#define W25Q_JEDEC_ID 0x9F /* <读3个字节分别是生产厂家、存储器类型、容量> */
HAL_StatusTypeDef W25Q16_Read_State_Reg(uint8_t Select, uint8_t *State);
/* <判忙> */
void W25Q16_Judge_Busy(void);
/* <写状态寄存器> */
HAL_StatusTypeDef W25Q16_Write_State_Reg(uint8_t * State);
/* <读数据> */
HAL_StatusTypeDef W25Q16_Read_Data(uint32_t Read_Address, uint8_t *Read_Data, uint16_t Read_Size);
/* <页写> */
HAL_StatusTypeDef W25Q16_Page_Write(uint32_t Write_Address, uint8_t * PW_Data, uint16_t PW_Size);
/* <扇区擦除> */
HAL_StatusTypeDef W25Q16_Sector_Erase(uint32_t Sector_Address);
/* <块擦除> */
HAL_StatusTypeDef W25Q16_Block_Erase(uint32_t Block_Address);
/* <全片擦除> */
HAL_StatusTypeDef W25Q16_Full_Erase(void);
/* <读ID> */
HAL_StatusTypeDef W25Q16_Read_Jedec_ID(uint8_t * Read_Jedec_ID);
/* <写使能> */
HAL_StatusTypeDef W25Q16_Write_Enable(void);
/* <写失能> */
HAL_StatusTypeDef W25Q16_Write_Disable(void);
#endif
下面是w25q16.c文件
#include "w25q16.h"
/**
* @brief 将SPI发送函数封装
* @param *Tr_Data: 要发送的缓冲区
* @param 要发送数据的长度
* @retval
*/
static HAL_StatusTypeDef W25Q16_Transmit(uint8_t * Tr_Data, uint16_t Tr_Size)
{
return HAL_SPI_Transmit(&W25Q_SPI, Tr_Data, Tr_Size, 0xff);
}
/**
* @brief 将SPI接收函数封装
* @param *Rr_pData: 接收数据并放置到接收数据缓冲区中
* @param Rr_Size: 需要接收的数据的长度
* @retval
*/
static HAL_StatusTypeDef W25Q16_Receive(uint8_t * Re_Data, uint16_t Re_Size)
{
return HAL_SPI_Receive(&W25Q_SPI, Re_Data, Re_Size, 0xff);
}
/**
* @brief 写使能或失能
* @param Type: 为1时使能; 为0时失能
* @param 无
* @retval Ret: 0(使能成功), 1(使能失败)
*/
HAL_StatusTypeDef W25Q16_Write_Enable(void)
{
HAL_StatusTypeDef Ret = HAL_ERROR;
uint8_t CMD;
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
CMD = W25Q_WRITE_ENABLE;
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{Ret = HAL_OK;}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
return Ret;
}
HAL_StatusTypeDef W25Q16_Write_Disable(void)
{
uint8_t CMD;
HAL_StatusTypeDef Ret = HAL_ERROR;
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
CMD = W25Q_WRITE_DISABLE;
if(W25Q16_Transmit(&CMD, 1) == HAL_OK){Ret = HAL_OK;}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
return Ret;
}
/**
*@brief 读状态寄存器(flash接收命令后处于,需要时间处理命令,会有忙状态,flash无法接收命令,只能给MCU读取是否在忙)
*@param Select:为0时是寄存器1; 为1时是寄存器2
*@param State(指针):返回的状态标志
*@retval Ret: 0(读状态寄存器成功), 1(读状态寄存器失败)
*/
HAL_StatusTypeDef W25Q16_Read_State_Reg(uint8_t Select, uint8_t *State)
{
HAL_StatusTypeDef Ret = HAL_ERROR;
uint8_t CMD[4] = {0,0,0,0};
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
switch(Select){
case 0:CMD[0] = W25Q_READ_STATA_REG1;
break;
case 1:CMD[0] = W25Q_READ_STATA_REG2;
break;
default: ;}
if(W25Q16_Transmit(CMD, 4) == HAL_OK)
{
if(W25Q16_Receive(State,1) == HAL_OK)
{
Ret = HAL_OK;
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
return Ret;
}
/**
* @brief 判忙(阻塞等待Flash处于空闲状态)
* @param 无
* @param 无
* @retval 无
*/
void W25Q16_Judge_Busy(void)
{
uint8_t State;
do{
W25Q16_Read_State_Reg(0, &State); //不要用指针类型局部变量传进去,必被卡死
State &= 0x01;
}while(State == 0x01);
}
/**
*@brief 写状态寄存器
*@param State(数组指针): (长度为两个字节的数组指针) 第一个字节写入状态寄存器1;第二个字节写入状态寄存器2。
*@param 无
*@retval Ret: 0(写状态寄存器成功), 1(写状态寄存器失败)
*/
HAL_StatusTypeDef W25Q16_Write_State_Reg(uint8_t * State)
{
uint8_t CMD = W25Q_WRITE_STATA_REG;
HAL_StatusTypeDef Ret = HAL_ERROR;
W25Q16_Judge_Busy();
W25Q16_Write_Enable();
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
if(W25Q16_Transmit(State, 2) == HAL_OK)
{
Ret = HAL_OK;
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
W25Q16_Write_Disable();
return Ret;
}
/**
* @brief 读取数据
* @param R_Addr : 读取数据的地址
* @param R_Data(数组指针) : 获取读取的数据
* @param R_Size : 读取的数据的大小
* @retval Ret: 0(读取数据成功), 1(读取数据失败)
*/
HAL_StatusTypeDef W25Q16_Read_Data(uint32_t Read_Address, uint8_t *Read_Data, uint16_t Read_Size)
{
uint8_t CMD = W25Q_READ_DATA;
HAL_StatusTypeDef Ret = HAL_ERROR;
Read_Address <<= 8; //只要24位,3个字节
W25Q16_Judge_Busy(); //判忙
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
if(W25Q16_Transmit((uint8_t *)&Read_Address, 3) == HAL_OK)
{
if(W25Q16_Receive(Read_Data,Read_Size) == HAL_OK)
{
Ret = HAL_OK;
}
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
return Ret;
}
/**
* @brief 写页(步骤:先判忙, 再使能, 判忙,再使能片选, 再写命令, 再写3个字节的地址,后写入数据,最后写失能)
* @param WriteAddr: 写入的地址(三个字节地址)
* @param PW_Data(数组指针): 要写入的数据(高位先传)
* @param PW_Size: 要写入的数据长度
* @retval Ret: 0(写页成功), 1(写页失败)
*/
HAL_StatusTypeDef W25Q16_Page_Write(uint32_t Write_Address, uint8_t * PW_Data, uint16_t PW_Size)
{
HAL_StatusTypeDef Ret = HAL_ERROR;
uint8_t CMD = W25Q_Page_Program;/* <左移8位是因为只需要24位,总共32位左移8位后低8位都是0> */
Write_Address <<= 8; //只要24位,3个字节
W25Q16_Write_Enable();
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
if(W25Q16_Transmit((uint8_t *)&Write_Address, 3) == HAL_OK)
{
if(W25Q16_Transmit(PW_Data, PW_Size) == HAL_OK)
{
Ret = HAL_OK;
}
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
W25Q16_Judge_Busy();
return Ret;
}
/**
* @brief 扇区擦除(W25Q64总共8MB,分为128块(每块64KB),每块16个扇区,每个扇区4K个字节。)
* @param Sector_Addr: 擦除地址(以4KB为单位寻址, 高位先发)
* @param 无
* @retval
*/
HAL_StatusTypeDef W25Q16_Sector_Erase(uint32_t Sector_Address)
{
uint8_t CMD = W25Q_Sector_Erase;
HAL_StatusTypeDef Ret = HAL_ERROR;/* <每个块有16个扇区(一个扇区有4KB的大小),需要换算为实际地址(为了使找到对应的扇区地址,所以要乘以4KB)> */
Sector_Address *= 4096;/* <只需要24位表示地址,并且高位先传> */
Sector_Address <<= 8;
W25Q16_Write_Enable();
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
if(W25Q16_Transmit((uint8_t *)&Sector_Address, 3) == HAL_OK)
{
Ret = HAL_OK;
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
W25Q16_Judge_Busy();
return Ret;
}
/**
* @brief 块擦除(W25Q64有8MB的容量,而8MB有128个块,所以1块有64kB的大小,所以这个函数一次能擦除64KB的大小)
* @param Block_Addr: (1、块地址,共128个块,对应128个地址,以64K为单位寻址2、高位先传)
* @param 无
* @retval
*/
HAL_StatusTypeDef W25Q16_Block_Erase(uint32_t Block_Address)
{
uint8_t CMD = W25Q_Block_Erase;HAL_StatusTypeDef Ret = HAL_ERROR;//总共有128个块,而一个块有64KB的大小,//为了使找到对应的块地址,所以要乘以64KB
Block_Address *= (1<<16);Block_Address <<= 8; //只需要24位表示地址,并且高位先传
W25Q16_Write_Enable();
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
if(W25Q16_Transmit((uint8_t *)&Block_Address, 3) == HAL_OK)
{
Ret = HAL_OK;
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
W25Q16_Judge_Busy();
return Ret;
}
/**
* @brief 全片擦除(该函数尽量少使用)
* @param
* @param
* @retval
*/
HAL_StatusTypeDef W25Q16_Full_Erase(void)
{
HAL_StatusTypeDef Ret = HAL_ERROR;
uint8_t CMD = W25Q_Full_Erase;W25Q16_Write_Enable();
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
Ret = HAL_OK;
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
W25Q16_Judge_Busy();
return Ret;
}
/**
* @brief 读取 W25Q16 ID (读3个字节分别是生产厂家、存储器类型、容量)
* @param *R_Jedec_ID: 保存W25Q16 ID 的数组
* @param 无
* @retval
*/
HAL_StatusTypeDef W25Q16_Read_Jedec_ID(uint8_t * Read_Jedec_ID)
{
uint8_t CMD = W25Q_JEDEC_ID;
HAL_StatusTypeDef Ret = HAL_ERROR;
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_RESET);
if(W25Q16_Transmit(&CMD, 1) == HAL_OK)
{
if(W25Q16_Receive(Read_Jedec_ID, 3) == HAL_OK)
{
Ret = HAL_OK;
}
}
HAL_GPIO_WritePin(W25Q_CS_Port, W25Q_CS_Pin, GPIO_PIN_SET);
return Ret;
}
修改正点原子保存校准数据函数与数据保存地址
/* TP_SAVE_ADDR_BASE定义触摸屏校准参数保存在w25q16里面的位置(起始地址)
* 占用空间 : 13字节.
*/
#define TP_SAVE_ADDR_BASE 0x000001
/**
* @brief 保存校准参数
* @note 参数保存在flash芯片里面(w25q16),起始地址为TP_SAVE_ADDR_BASE.
* 占用大小为13字节
* @param 无
* @retval 无
*/
void tp_save_adjust_data(void)
{
uint8_t *p = (uint8_t *)&tp_dev.xfac; /* 指向首地址 */
/* p指向tp_dev.xfac的地址, p+4则是tp_dev.yfac的地址
* p+8则是tp_dev.xoff的地址,p+10,则是tp_dev.yoff的地址
* 总共占用12个字节(4个参数)
* p+12用于存放标记电阻触摸屏是否校准的数据(0X0A)
* 往p[12]写入0X0A. 标记已经校准过.
*/
W25Q16_Sector_Erase(TP_SAVE_ADDR_BASE);
W25Q16_Page_Write(TP_SAVE_ADDR_BASE, p, 13); //保存13个字节数据(xfac,yfac,xc,yc,flag)
}
修改获取校准数据函数
/**
* @brief 获取保存在EEPROM里面的校准值
* @param 无
* @retval 0,获取失败,要重新校准
* 1,成功获取数据
*/
uint8_t tp_get_adjust_data(void)
{
uint8_t *p = (uint8_t *)&tp_dev.xfac;
uint8_t temp = 0;
/* 由于我们是直接指向tp_dev.xfac地址进行保存的, 读取的时候,将读取出来的数据
* 写入指向tp_dev.xfac的首地址, 就可以还原写入进去的值, 而不需要理会具体的数
* 据类型. 此方法适用于各种数据(包括结构体)的保存/读取(包括结构体).
*/
W25Q16_Read_Data(TP_SAVE_ADDR_BASE, p, 13); /* 读取13字节数据 */
temp = *(p+12);
if (temp == 0X0A)
{
return 1;
}
return 0;
}
修改tp_init(void)屏幕初始函数,注释掉24c02的初始化函数,添加自己的w25qxx初始化函数。
//at24cxx_init(); /* 初始化24CXX */
MX_SPI1_Init(); /* 初始化w25q16 */
修改touch.h的触摸屏控制器结构体
/* 5点校准触摸屏校准参数(电容屏不需要校准) */
float xfac; /* 5点校准法x方向比例因子 */
float yfac; /* 5点校准法y方向比例因子 */
short xc; /* 中心X坐标物理值(AD值) */
short yc; /* 中心Y坐标物理值(AD值) */
在结构体中添加一个参数flag,用来存放是否校准标志
/* 5点校准触摸屏校准参数(电容屏不需要校准) */
float xfac; /* 5点校准法x方向比例因子 */
float yfac; /* 5点校准法y方向比例因子 */
short xc; /* 中心X坐标物理值(AD值) */
short yc; /* 中心Y坐标物理值(AD值) */
uint8_t flag;
在touch.c中_m_tp_dev tp_dev加多一个0
_m_tp_dev tp_dev =
{
tp_init,
tp_scan,
tp_adjust,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
修改完成后,就可以用w25q16来存储屏幕校准数据。