#include <string.h>
#include <stdlib.h>
#include "stdio.h"
#include "at24cxx.h"
#ifdef PKG_USING_AT24CXX
//#define DRV_DEBUG
#ifdef DRV_DEBUG
#define LOG_E(...) lwlog_debug(__VA_ARGS__, "")
#define LOG_D(...) lwlog_debug(__VA_ARGS__, "")
#else
#define LOG_E(...)
#define LOG_D(...)
#endif
#define AT24CXX_ADDR (0xA0) //A0 A1 A2 connect GND
#if (EE_TYPE == AT24C01)
#define AT24CXX_PAGE_BYTE 8
#define AT24CXX_MAX_MEM_ADDRESS 128
#elif (EE_TYPE == AT24C02)
#define AT24CXX_PAGE_BYTE 8
#define AT24CXX_MAX_MEM_ADDRESS 256
#elif (EE_TYPE == AT24C04)
#define AT24CXX_PAGE_BYTE 16
#define AT24CXX_MAX_MEM_ADDRESS 512
#elif (EE_TYPE == AT24C08)
#define AT24CXX_PAGE_BYTE 16
#define AT24CXX_MAX_MEM_ADDRESS 1024
#elif (EE_TYPE == AT24C16)
#define AT24CXX_PAGE_BYTE 16
#define AT24CXX_MAX_MEM_ADDRESS 2048
#elif (EE_TYPE == AT24C32)
#define AT24CXX_PAGE_BYTE 32
#define AT24CXX_MAX_MEM_ADDRESS 4096
#elif (EE_TYPE == AT24C64)
#define AT24CXX_PAGE_BYTE 32
#define AT24CXX_MAX_MEM_ADDRESS 8192
#elif (EE_TYPE == AT24C128)
#define AT24CXX_PAGE_BYTE 64
#define AT24CXX_MAX_MEM_ADDRESS 16384
#elif (EE_TYPE == AT24C256)
#define AT24CXX_PAGE_BYTE 64
#define AT24CXX_MAX_MEM_ADDRESS 32768
#elif (EE_TYPE == AT24C512)
#define AT24CXX_PAGE_BYTE 128
#define AT24CXX_MAX_MEM_ADDRESS 65536
#endif
/** 函数名:
* 功能 :
* 输入 :
* 输出 :
* 编写人:XMW
*/
const static char *EE_Type_Str[] =
{
"AT24C01",
"AT24C02",
"AT24C04",
"AT24C08",
"AT24C16",
"AT24C32",
"AT24C64",
"AT24C128",
"AT24C256",
"AT24C512",
"AT24CTYPE"
};
/** 函数名:rt_hw_ms_delay
* 功能 :
* 输入 :
* 输出 :
* 编写人:XMW
*/
void rt_hw_ms_delay(uint32_t ms)
{
osDelay(ms);
}
/** 函数名:read_regs
* 功能 :读取寄存器
* 输入 :
* 输出 :
* 编写人:XMW
*/
static uint8_t read_regs(at24cxx_device_t dev, uint8_t len, uint8_t *buf)
{
if (HAL_I2C_Master_Receive(dev->i2c,AT24CXX_ADDR,buf,len,10) == HAL_OK)
{
return AT_EOK;
}
else
{
return AT_ERROR;
}
}
/** 函数名:at24cxx_read_one_byte
* 功能 :
* 输入 :
* 输出 :
* 编写人:XMW
*/
uint8_t at24cxx_read_one_byte(at24cxx_device_t dev, uint16_t readAddr)
{
uint8_t buf[2];
uint8_t temp;
if(dev->DevType.eeType > AT24C16)
{
buf[0] = (uint8_t)(readAddr>>8);
buf[1] = (uint8_t)readAddr;
if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR, buf, 2 ,10) != 0)
{
return AT_ERROR;
}
}
else
{
buf[0] = readAddr;
if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR|(readAddr>>7), buf, 1,10) != 0)
{
return AT_ERROR;
}
}
//#if (EE_TYPE > AT24C16)
// buf[0] = (uint8_t)(readAddr>>8);
// buf[1] = (uint8_t)readAddr;
// if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR, buf, 2 ,10) != 0)
//#else
// buf[0] = readAddr;
// if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR|(readAddr>>7), buf, 1,10) != 0)
//#endif
// {
// return AT_ERROR;
// }
read_regs(dev, 1, &temp);
return temp;
}
/** 函数名:at24cxx_write_one_byte
* 功能 :
* 输入 :
* 输出 :
* 编写人:XMW
*/
uint8_t at24cxx_write_one_byte(at24cxx_device_t dev, uint16_t writeAddr, uint8_t dataToWrite)
{
uint8_t buf[3];
if(dev->DevType.eeType > AT24C16)
{
buf[0] = (uint8_t)(writeAddr>>8);
buf[1] = (uint8_t)writeAddr;
buf[2] = dataToWrite;
if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR, buf, 3,10) == 0)
{
return AT_EOK;
}
}
else
{
buf[0] = writeAddr; //cmd
buf[1] = dataToWrite;
if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR|(writeAddr>>7), buf, 2 , 10) == 0)
{
return AT_EOK;
}
}
return AT_ERROR;
//#if (EE_TYPE > AT24C16)
// buf[0] = (uint8_t)(writeAddr>>8);
// buf[1] = (uint8_t)writeAddr;
// buf[2] = dataToWrite;
// if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR, buf, 3,10) == 0)
//#else
// buf[0] = writeAddr; //cmd
// buf[1] = dataToWrite;
// if (HAL_I2C_Master_Transmit(dev->i2c, AT24CXX_ADDR|(writeAddr>>7), buf, 2 , 10) == 0)
//#endif
// return AT_EOK;
// else
// return AT_ERROR;
}
/** 函数名:at24cxx_read_page
* 功能 :
* 输入 :
* 输出 :
* 编写人:XMW
*/
uint8_t at24cxx_read_page(at24cxx_device_t dev, uint32_t readAddr, uint8_t *pBuffer, uint16_t numToRead)
{
HAL_StatusTypeDef status;
if (HAL_I2C_IsDeviceReady(dev->i2c, dev->AddrInput, 2, 0x1000) != HAL_OK)
{
HAL_I2C_DeInit(dev->i2c);
HAL_I2C_Init(dev->i2c);
}
if(dev->DevType.eeType > AT24C16)
{
status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput,readAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToRead,20);
}
else
{
status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput|(readAddr>>7),readAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToRead,20);
}
//#if (EE_TYPE > AT24C16)
// status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput,readAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToRead,20);
//#else
// status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput|(readAddr>>7),readAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToRead,20);
//#endif
if(status == HAL_OK)
{
return AT_EOK;
}
else
{
HAL_I2C_DeInit(dev->i2c);
HAL_I2C_Init(dev->i2c);
if(dev->DevType.eeType > AT24C16)
{
status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput,readAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToRead,20);
}
else
{
status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput|(readAddr>>7),readAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToRead,20);
}
// #if (EE_TYPE > AT24C16)
// status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput,readAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToRead,20);
// #else
// status = HAL_I2C_Mem_Read(dev->i2c,dev->AddrInput|(readAddr>>7),readAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToRead,20);
// #endif
return AT_ERROR;
}
// while (HAL_I2C_IsDeviceReady(dev->i2c, dev->AddrInput, 2, 0x1000) != HAL_OK){};
}
uint8_t at24cxx_write_page(at24cxx_device_t dev, uint32_t wirteAddr, uint8_t *pBuffer, uint16_t numToWrite)
{
HAL_StatusTypeDef status;
if (HAL_I2C_IsDeviceReady(dev->i2c, dev->AddrInput, 2, 0x1000) != HAL_OK)
{
HAL_I2C_DeInit(dev->i2c);
HAL_I2C_Init(dev->i2c);
}
if(dev->DevType.eeType > AT24C16)
{
status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput,wirteAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToWrite,20);
}
else
{
status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput|(wirteAddr>>7),wirteAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToWrite,20);
}
//#if (EE_TYPE > AT24C16)
// status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput,wirteAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToWrite,20);
//#else
// status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput|(wirteAddr>>7),wirteAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToWrite,20);
//#endif
if(status != HAL_OK)
{
HAL_I2C_DeInit(dev->i2c);
HAL_I2C_Init(dev->i2c);
if(dev->DevType.eeType > AT24C16)
{
status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput,wirteAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToWrite,20);
}
else
{
status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput|(wirteAddr>>7),wirteAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToWrite,20);
}
// #if (EE_TYPE > AT24C16)
// status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput,wirteAddr,I2C_MEMADD_SIZE_16BIT,pBuffer,numToWrite,20);
// #else
// status = HAL_I2C_Mem_Write(dev->i2c,dev->AddrInput|(wirteAddr>>7),wirteAddr,I2C_MEMADD_SIZE_8BIT,pBuffer,numToWrite,20);
// #endif
return AT_ERROR;
}
else
{
return AT_EOK;
}
// while (HAL_I2C_IsDeviceReady(dev->i2c, dev->AddrInput, 2, 0x1000) != HAL_OK){};
}
static uint8_t at24cxx_check_page(at24cxx_device_t dev, uint32_t maxAdress)
{
// for(uint32_t i=8;i>0;i--)
// {
// if(at24cxx_check_device(dev, uint32_t maxAdress))
//
// }
}
static uint8_t at24cxx_check_device(at24cxx_device_t dev, uint32_t maxAdress)
{
volatile uint8_t temp = 0x0 ,old_data, oldtemp;
if(dev == NULL)
return AT_ERROR;
rt_hw_ms_delay(EE_TWR);
for(uint8_t i=1;i<=8;i++)
{
old_data = at24cxx_read_one_byte(dev, maxAdress - i);
rt_hw_ms_delay(EE_TWR);
oldtemp = i;
at24cxx_write_one_byte(dev, maxAdress - i, oldtemp);
rt_hw_ms_delay(EE_TWR);
temp = at24cxx_read_one_byte(dev, maxAdress - i);
rt_hw_ms_delay(EE_TWR);
at24cxx_write_one_byte(dev, maxAdress - i, old_data);
if (temp != oldtemp)
{
return AT_ERROR;
}
rt_hw_ms_delay(EE_TWR);
}
return AT_EOK;
}
uint32_t at24cxx_check_poll(at24cxx_device_t dev)
{
uint32_t addrMax = 0;
int8_t eeTypeId = AT24CTYPE;
#ifdef EEPROM_AUTO_CHECK
for(int8_t i=AT24CTYPE-1;i>=0;i--)
{
addrMax = pow(2,i)*128;
dev->DevType.eeType = i;
if(at24cxx_check_device(dev, addrMax)==AT_EOK)
{
eeTypeId = i;
break;
}
}
#endif
if(eeTypeId >= AT24CTYPE)
{
dev->DevType.eeType = EE_TYPE;
dev->DevType.memSize = AT24CXX_MAX_MEM_ADDRESS;
dev->DevType.pageSize = AT24CXX_PAGE_BYTE;
}
else
{
dev->DevType.memSize = addrMax;
if(dev->DevType.eeType <= AT24C02)
dev->DevType.pageSize = 8;
else if(dev->DevType.eeType <= AT24C16)
dev->DevType.pageSize = 16;
else if(dev->DevType.eeType <= AT24C64)
dev->DevType.pageSize = 32;
else if(dev->DevType.eeType <= AT24C256)
dev->DevType.pageSize = 64;
else
dev->DevType.pageSize = 128;
}
lwlog_debug("at24cxx:%s, %d,%d.",EE_Type_Str[eeTypeId],dev->DevType.pageSize, dev->DevType.memSize);
// dev->eeType = EE_TYPE;
}
uint8_t at24cxx_check(at24cxx_device_t dev)
{
volatile uint8_t temp = 0x0 ,old_data;
if(dev == NULL)
return AT_ERROR;
rt_hw_ms_delay(EE_TWR);
at24cxx_check_poll(dev);
rt_hw_ms_delay(EE_TWR);
old_data = at24cxx_read_one_byte(dev, dev->DevType.memSize - 1);
rt_hw_ms_delay(EE_TWR);
at24cxx_write_one_byte(dev, dev->DevType.memSize - 1, 0x55);
rt_hw_ms_delay(EE_TWR);
temp = at24cxx_read_one_byte(dev, dev->DevType.memSize - 1);
if (temp == 0x55)
{
at24cxx_write_one_byte(dev, dev->DevType.memSize - 1, old_data);
rt_hw_ms_delay(EE_TWR);
return AT_EOK;
}
return AT_ERROR;
}
/**
* This function read the specific numbers of data to the specific position
*
* @param bus the name of at24cxx device
* @param ReadAddr the start position to read
* @param pBuffer the read data store position
* @param NumToRead
* @return AT_EOK write ok.
*/
uint8_t at24cxx_read(at24cxx_device_t dev, uint32_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead)
{
uint8_t result = AT_EOK;
if(dev == NULL)
result = AT_ERROR;
if(ReadAddr + NumToRead > AT24CXX_MAX_MEM_ADDRESS)
{
result = AT_ERROR;
}
osMutexAcquire(dev->lock,osWaitForever);
if (result == AT_EOK)
{
while (NumToRead)
{
*pBuffer++ = at24cxx_read_one_byte(dev, ReadAddr++);
NumToRead--;
}
}
else
{
LOG_E("The at24cxx could not respond at this time. Please try again");
}
osMutexRelease(dev->lock);
return result;
}
/**
* This function read the specific numbers of data to the specific position
*
* @param bus the name of at24cxx device
* @param ReadAddr the start position to read
* @param pBuffer the read data store position
* @param NumToRead
* @return AT_EOK write ok.
*/
uint8_t at24cxx_page_read(at24cxx_device_t dev, uint32_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead)
{
uint8_t result = AT_EOK;
uint16_t pageReadSize = dev->DevType.pageSize - ReadAddr % dev->DevType.pageSize;
if(ReadAddr + NumToRead > dev->DevType.memSize)
{
return AT_ERROR;
}
osMutexAcquire(dev->lock,osWaitForever);
if(result == AT_EOK)
{
while (NumToRead)
{
if(NumToRead > pageReadSize)
{
if(at24cxx_read_page(dev, ReadAddr, pBuffer, pageReadSize))
{
result = AT_ERROR;
}
// rt_hw_ms_delay(EE_TWR);
ReadAddr += pageReadSize;
pBuffer += pageReadSize;
NumToRead -= pageReadSize;
pageReadSize = dev->DevType.pageSize;
}
else
{
if(at24cxx_read_page(dev, ReadAddr, pBuffer, NumToRead))
{
result = AT_ERROR;
}
// rt_hw_ms_delay(EE_TWR);
NumToRead = 0;
}
}
}
else
{
LOG_E("The at24cxx could not respond at this time. Please try again");
}
osMutexRelease(dev->lock);
return result;
}
/**
* This function write the specific numbers of data to the specific position
*
* @param bus the name of at24cxx device
* @param WriteAddr the start position to write
* @param pBuffer the data need to write
* @param NumToWrite
* @return AT_EOK write ok.at24cxx_device_t dev
*/
uint8_t at24cxx_write(at24cxx_device_t dev, uint32_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite)
{
uint16_t i = 0;
uint8_t result= AT_EOK;
if(dev == NULL)
result= AT_ERROR;
if(WriteAddr + NumToWrite > AT24CXX_MAX_MEM_ADDRESS)
{
result= AT_ERROR;
}
osMutexAcquire(dev->lock,osWaitForever);
if (result == AT_EOK)
{
while (1) //NumToWrite--
{
if (at24cxx_write_one_byte(dev, WriteAddr, pBuffer[i]) != AT_EOK)
{
rt_hw_ms_delay(EE_TWR);
}
else
{
WriteAddr++;
i++;
}
if (i == NumToWrite)
{
break;
}
}
}
else
{
LOG_E("The at24cxx could not respond at this time. Please try again");
}
osMutexRelease(dev->lock);
return result;
}
/**
* This function write the specific numbers of data to the specific position
*
* @param bus the name of at24cxx device
* @param WriteAddr the start position to write
* @param pBuffer the data need to write
* @param NumToWrite
* @return AT_EOK write ok.at24cxx_device_t dev
*/
uint8_t at24cxx_page_write(at24cxx_device_t dev, uint32_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite)
{
uint8_t result = AT_EOK;
uint16_t pageWriteSize = dev->DevType.pageSize - WriteAddr % dev->DevType.pageSize;
if(WriteAddr + NumToWrite > dev->DevType.memSize)
{
return AT_ERROR;
}
osMutexAcquire(dev->lock,osWaitForever);
if(result == AT_EOK)
{
while (NumToWrite)
{
if(NumToWrite > pageWriteSize)
{
if(at24cxx_write_page(dev, WriteAddr, pBuffer, pageWriteSize))
{
result = AT_ERROR;
}
rt_hw_ms_delay(EE_TWR); // wait 5ms befor next operation
WriteAddr += pageWriteSize;
pBuffer += pageWriteSize;
NumToWrite -= pageWriteSize;
pageWriteSize = dev->DevType.pageSize;
}
else
{
if(at24cxx_write_page(dev, WriteAddr, pBuffer, NumToWrite))
{
result = AT_ERROR;
}
rt_hw_ms_delay(EE_TWR); // wait 5ms befor next operation
NumToWrite = 0;
}
}
}
else
{
LOG_E("The at24cxx could not respond at this time. Please try again");
}
osMutexRelease(dev->lock);
return result;
}
uint8_t pageErase[128];
/**
* This function write the specific numbers of data to the specific position
*
* @param bus the name of at24cxx device
* @param WriteAddr the start position to write
* @param pBuffer the data need to write
* @param NumToWrite
* @return AT_EOK write ok.at24cxx_device_t dev
*/
uint8_t at24cxx_page_erase(at24cxx_device_t dev, uint32_t WriteAddr, uint16_t NumToWrite)
{
uint8_t result = AT_EOK;
uint16_t pageWriteSize = AT24CXX_PAGE_BYTE - WriteAddr % AT24CXX_PAGE_BYTE;
if(WriteAddr + NumToWrite > AT24CXX_MAX_MEM_ADDRESS)
{
return AT_ERROR;
}
osMutexAcquire(dev->lock,osWaitForever);
if(result == AT_EOK)
{
while (NumToWrite)
{
if(NumToWrite > pageWriteSize)
{
if(at24cxx_write_page(dev, WriteAddr, pageErase, pageWriteSize))
{
result = AT_ERROR;
}
rt_hw_ms_delay(EE_TWR); // wait 5ms befor next operation
WriteAddr += pageWriteSize;
// pBuffer += pageWriteSize;
NumToWrite -= pageWriteSize;
pageWriteSize = AT24CXX_PAGE_BYTE;
}
else
{
if(at24cxx_write_page(dev, WriteAddr, pageErase, NumToWrite))
{
result = AT_ERROR;
}
rt_hw_ms_delay(EE_TWR); // wait 5ms befor next operation
NumToWrite = 0;
}
}
}
else
{
LOG_E("The at24cxx could not respond at this time. Please try again");
}
osMutexRelease(dev->lock);
return result;
}
/**
* This function initializes at24cxx registered device driver
*
* @param dev the name of at24cxx device
*
* @return the at24cxx device.
*/
static struct at24cxx_device atxdevice;
at24cxx_device_t at24cxx_init(I2C_HandleTypeDef *hi2c, uint8_t AddrInput)
{
at24cxx_device_t dev;
dev = &atxdevice;
dev->i2c = hi2c;
dev->lock = osMutexNew(NULL);
dev->AddrInput = AT24CXX_ADDR;
dev->DevType.eeType = EE_TYPE;
if (at24cxx_check(dev) != AT_EOK)
{
if(dev->lock != NULL)
osMutexDelete(dev->lock);
dev = NULL;
}
return dev;
}
/**
* This function releases memory and deletes mutex lock
*
* @param dev the pointer of device driver structure
*/
void at24cxx_deinit(at24cxx_device_t dev)
{
if(dev->lock != NULL)
osMutexDelete(dev->lock);
dev = NULL;
}
#endif
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-13 XiaojieFan the first version
* 2019-12-04 RenMing Use PAGE WRITE instead of BYTE WRITE and input address can be selected
*/
#ifndef __AT24CXX_H__
#define __AT24CXX_H__
#include "main.h"
#define AT24C01 0
#define AT24C02 1
#define AT24C04 2
#define AT24C08 3
#define AT24C16 4
#define AT24C32 5
#define AT24C64 6
#define AT24C128 7
#define AT24C256 8
#define AT24C512 9
#define AT24CTYPE 10 // Number of supported types
#define EE_TWR 5
#ifndef EE_TYPE
#define EE_TYPE AT24C128
#endif
typedef enum
{
AT_EOK = 1,
AT_ERROR = 2
}at24cxx_s;
typedef struct
{
uint8_t eeType;
uint8_t pageSize;
uint32_t memSize;
}at24cxxType_t;
struct at24cxx_device
{
I2C_HandleTypeDef *i2c;
osMutexId_t lock;
uint8_t AddrInput;
at24cxxType_t DevType;
};
typedef struct at24cxx_device *at24cxx_device_t;
void at24cxx_deinit(at24cxx_device_t dev);
at24cxx_device_t at24cxx_init(I2C_HandleTypeDef *hi2c, uint8_t AddrInput);
uint8_t at24cxx_read(at24cxx_device_t dev, uint32_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead);
uint8_t at24cxx_write(at24cxx_device_t dev, uint32_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite);
uint8_t at24cxx_page_read(at24cxx_device_t dev, uint32_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead);
uint8_t at24cxx_page_write(at24cxx_device_t dev, uint32_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite);
uint8_t at24cxx_page_erase(at24cxx_device_t dev, uint32_t WriteAddr, uint16_t NumToWrite);
#endif
Comments | NOTHING