modules/sht20.c | ●●●●● patch | view | raw | blame | history | |
modules/w25qflash.c | ●●●●● patch | view | raw | blame | history | |
modules/w25qflash.h | ●●●●● patch | view | raw | blame | history |
modules/sht20.c
@@ -20,7 +20,6 @@ * dtoverlay=i2c1,pins_2_3 * ********************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -39,11 +38,37 @@ #include <linux/i2c.h> #include <linux/i2c-dev.h> int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len); int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size); int sht20_checksum(uint8_t *data, int len, int8_t checksum); /* SHT2X 7-bit I2C slave address */ #define SHT2X_CHIPADDR 0x40 /* SHT2X trigger command */ #define SOFTRESET 0xFE #define TRIGGER_TEMPERATURE_NO_HOLD 0xF3 #define TRIGGER_HUMIDITY_NO_HOLD 0xF5 /* Linux /dev/i2c-x device program API mode */ enum { MODE_IOCTL, /* I2C device API use ioctl() */ MODE_RDWR, /* I2C device API use read()/write() */ }; typedef struct sht2x_s { char *dev; /* SHT20 connect I2C device, /dev/i2c-N */ int addr; /* SHT20 7-bits slave address */ int fd; /* File description */ int mode; /* I2C device API use read/write or ioctl mode */ } sht2x_t; int sht2x_init(sht2x_t *sht2x); int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size); int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh); static inline void msleep(unsigned long ms); static inline void dump_buf(const char *prompt, uint8_t *buf, int size); static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len); static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size); static inline void banner(const char *progname) { @@ -58,6 +83,7 @@ printf(" %s is SHT20 temperature and humidity sensor program. \n", progname); printf(" -d[device ] Specify I2C device, such as /dev/i2c-1\n"); printf(" -m[mode ] Specify API mode, 0 for ioctl and 1 for read()/write()\n"); printf(" -h[help ] Display this help information\n"); printf(" -v[version ] Display the program version\n"); @@ -68,14 +94,15 @@ int main(int argc, char **argv) { int fd, rv; int rv; float temp, rh; char *dev = "/dev/i2c-1"; char *progname=NULL; uint8_t buf[4]; uint8_t serialnumber[8]; char *progname=NULL; sht2x_t sht2x; struct option long_options[] = { {"device", required_argument, NULL, 'd'}, {"mode", required_argument, NULL, 'm'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} @@ -83,13 +110,21 @@ progname = basename(argv[0]); memset(&sht2x, 0, sizeof(sht2x)); sht2x.addr = SHT2X_CHIPADDR; sht2x.dev = "/dev/i2c-1"; /* Parser the command line parameters */ while ((rv = getopt_long(argc, argv, "d:vh", long_options, NULL)) != -1) while ((rv = getopt_long(argc, argv, "d:m:vh", long_options, NULL)) != -1) { switch (rv) { case 'd': /* Set I2C device path: /dev/i2c-1 */ dev = optarg; sht2x.dev = optarg; break; case 'm': /* Set I2C API mode: 0,1 */ sht2x.mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL; break; case 'v': /* Get software version */ @@ -105,108 +140,165 @@ } } /*+--------------------------------+ *| open /dev/i2c-x device | *+--------------------------------+*/ if( (fd=open(dev, O_RDWR)) < 0) if( sht2x_init(&sht2x) < 0 ) { printf("i2c device '%s' open failed: %s\n", dev, strerror(errno)); printf("SHT2x initialize failure, maybe device not present!\n"); return 1; } if( sht2x_get_serialnumber(&sht2x, serialnumber, 8) < 0) { printf("SHT2x get serial number failure\n"); return 3; } if( sht2x_get_temp_humidity(&sht2x, &temp, &rh) < 0 ) { printf("SHT2x get get temperature and relative humidity failure\n"); return 3; } printf("Temperature=%lf ℃ relative humidity=%lf.\n", temp, rh); close(sht2x.fd); } int sht2x_softreset(sht2x_t *sht2x) { uint8_t buf[1]; if( !sht2x || sht2x->fd<0 ) { printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); return -1; } /*+--------------------------------+ *| software reset SHT20 sensor | *+--------------------------------+*/ buf[0] = 0xFE; i2c_write(fd, 0x40, buf, 1); /* software reset SHT2x */ buf[0] = SOFTRESET; if( i2c_write(sht2x, buf, 1) < 0 ) { return -2; } msleep(50); return 0; } /*+--------------------------------+ *| trigger temperature measure | *+--------------------------------+*/ int sht2x_init(sht2x_t *sht2x) { if( (sht2x->fd=open(sht2x->dev, O_RDWR)) < 0) { printf("i2c device open failed: %s\n", strerror(errno)); return -1; } buf[0] = 0xF3; i2c_write(fd, 0x40, buf, 1); if( MODE_RDWR == sht2x->mode ) { /* set I2C mode and SHT2x slave address */ ioctl(sht2x->fd, I2C_TENBIT, 0); /* Not 10-bit but 7-bit mode */ ioctl(sht2x->fd, I2C_SLAVE, SHT2X_CHIPADDR); /* set SHT2x slave address 0x40*/ } if( sht2x_softreset(sht2x) < 0 ) { printf("SHT2x softreset failure\n"); return -2; } return 0; } int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh) { uint8_t buf[4]; if( !sht2x || !temp || !rh || sht2x->fd<0 ) { printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); return -1; } /* send trigger temperature measure command and read the data */ memset(buf, 0, sizeof(buf)); buf[0]=TRIGGER_TEMPERATURE_NO_HOLD; i2c_write(sht2x, buf, 1); msleep(85); /* datasheet: typ=66, max=85 */ memset(buf, 0, sizeof(buf)); i2c_read(fd, 0x40, buf, 3); i2c_read(sht2x, buf, 3); dump_buf("Temperature sample data: ", buf, 3); *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85; if( !sht20_checksum(buf, 2, buf[2]) ) { printf("Temperature sample data CRC checksum failure.\n"); goto cleanup; } temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85; /*+--------------------------------+ *| trigger humidity measure | *+--------------------------------+*/ buf[0] = 0xF5; i2c_write(fd, 0x40, buf, 1); /* send trigger humidity measure command and read the data */ memset(buf, 0, sizeof(buf)); buf[0] = TRIGGER_HUMIDITY_NO_HOLD; i2c_write(sht2x, buf, 1); msleep(29); /* datasheet: typ=22, max=29 */ memset(buf, 0, sizeof(buf)); i2c_read(fd, 0x40, buf, 3); i2c_read(sht2x, buf, 3); dump_buf("Relative humidity sample data: ", buf, 3); *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; if( !sht20_checksum(buf, 2, buf[2]) ) { printf("Relative humidity sample data CRC checksum failure.\n"); goto cleanup; } rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; /*+--------------------------------+ *| print the measure result | *+--------------------------------+*/ printf("Temperature=%lf 'C relative humidity=%lf%%\n", temp, rh); cleanup: close(fd); return 0; } int sht20_checksum(uint8_t *data, int len, int8_t checksum) int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size) { int8_t crc = 0; int8_t bit; int8_t byteCtr; uint8_t buf[4]={0x0}; //calculates 8-Bit checksum with given polynomial: x^8 + x^5 + x^4 + 1 for (byteCtr = 0; byteCtr < len; ++byteCtr) if( !sht2x || sht2x->fd<0 || !serialnumber || size!=8 ) { crc ^= (data[byteCtr]); for ( bit = 8; bit > 0; --bit) { /* x^8 + x^5 + x^4 + 1 = 0001 0011 0001 = 0x131 */ if (crc & 0x80) crc = (crc << 1) ^ 0x131; else crc = (crc << 1); } printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); return -1; } if (crc != checksum) return 0; else return 1; /* Read SerialNumber from Location 1 */ memset(buf, 0, sizeof(buf)); buf[0] = 0xfa; /* command for readout on-chip memory */ buf[1] = 0x0f; /* on-chip memory address */ i2c_write(sht2x, buf, 2); memset(buf, 0, sizeof(buf)); i2c_read(sht2x, buf, 4); if( !buf[0] && !buf[1] && !buf[2] && !buf[3] ) { printf("ERROR: SHT2X device not detected!\n"); return -2; } serialnumber[5]=buf[0]; /* Read SNB_3 */ serialnumber[4]=buf[1]; /* Read SNB_2 */ serialnumber[3]=buf[2]; /* Read SNB_1 */ serialnumber[2]=buf[3]; /* Read SNB_0 */ /* Read SerialNumber from Location 2 */ memset(buf, 0, sizeof(buf) ); buf[0]=0xfc; /* command for readout on-chip memory */ buf[1]=0xc9; /* on-chip memory address */ i2c_write(sht2x, buf, 2); memset(buf, 0, sizeof(buf) ); i2c_read(sht2x, buf, 4); serialnumber[1]=buf[0]; /* Read SNC_1 */ serialnumber[0]=buf[1]; /* Read SNC_0 */ serialnumber[7]=buf[2]; /* Read SNA_1 */ serialnumber[6]=buf[3]; /* Read SNA_0 */ dump_buf("SHT2x Serial number: ", serialnumber, 8); return 0; } int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len) static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len) { struct i2c_rdwr_ioctl_data i2cdata; int rv = 0; struct i2c_rdwr_ioctl_data i2cdata; int rv = 0; if( !data || len<= 0) { @@ -214,6 +306,14 @@ return -1; } /* I2C device program API: read()/write() */ if( MODE_RDWR == sht2x->mode ) { write(sht2x->fd, data, len); return 0; } /* I2C device program API: ioctl() */ i2cdata.nmsgs = 1; i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); if ( !i2cdata.msgs ) @@ -222,7 +322,7 @@ return -2; } i2cdata.msgs[0].addr = slave_addr; i2cdata.msgs[0].addr = sht2x->addr; i2cdata.msgs[0].flags = 0; //write i2cdata.msgs[0].len = len; i2cdata.msgs[0].buf = malloc(len); @@ -235,7 +335,7 @@ memcpy(i2cdata.msgs[0].buf, data, len); if( ioctl(fd, I2C_RDWR, &i2cdata)<0 ) if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 ) { printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); rv = -4; @@ -252,10 +352,10 @@ return rv; } int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size) static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size) { struct i2c_rdwr_ioctl_data i2cdata; int rv = 0; struct i2c_rdwr_ioctl_data i2cdata; int rv = 0; if( !buf || size<= 0) { @@ -263,6 +363,14 @@ return -1; } /* I2C device program API: read()/write() */ if( MODE_RDWR == sht2x->mode ) { read(sht2x->fd, buf, size); return 0; } /* I2C device program API: ioctl() */ i2cdata.nmsgs = 1; i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); if ( !i2cdata.msgs ) @@ -271,13 +379,13 @@ return -2; } i2cdata.msgs[0].addr = slave_addr; i2cdata.msgs[0].addr = sht2x->addr; i2cdata.msgs[0].flags = I2C_M_RD; //read i2cdata.msgs[0].len = size; i2cdata.msgs[0].buf = buf; memset(buf, 0, size); if( ioctl(fd, I2C_RDWR, &i2cdata)<0 ) if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 ) { printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); rv = -4; @@ -286,6 +394,10 @@ free( i2cdata.msgs ); return rv; } /*+----------------+ *| Misc functions | *+----------------+*/ static inline void msleep(unsigned long ms) { @@ -304,7 +416,6 @@ } nanosleep(&cSleep, 0); return ; } static inline void dump_buf(const char *prompt, uint8_t *buf, int size) @@ -318,7 +429,7 @@ if( prompt ) { printf("%-32s ", prompt); printf("%s ", prompt); } for(i=0; i<size; i++) modules/w25qflash.c
New file @@ -0,0 +1,845 @@ /********************************************************************************* * Copyright: (C) 2023 LingYun IoT System Studio. All Rights Reserved. * Author: Guo Wenxue <guowenxue@gmail.com> * * Description: This file is W25Qxx SPI Norflash driver on RaspberryPi 40Pin. * * W25QXX RaspberryPi 40Pin * VCC <---> 3.3V(Pin#1) * CS <---> CS(Pin#24) * DO <---> MISO(Pin#21) * GND <---> GND(Pin#9) * CLK <---> SCLK(Pin#23) * DI <---> MOSI(Pin#19) * ********************************************************************************/ #include <stdint.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <getopt.h> #include <fcntl.h> #include <time.h> #include <sys/ioctl.h> #include <linux/ioctl.h> #include <sys/stat.h> #include <linux/types.h> #include <linux/spi/spidev.h> #include "w25qflash.h" #define CONFIG_SPINOR_DEBUG #ifdef CONFIG_SPINOR_DEBUG #include <stdio.h> #define spinor_print(format,args...) printf(format, ##args) #else #define spinor_print(format,args...) do{} while(0) #endif #define spinor_Delay(delay) usleep(delay*1000) /*+-----------------------+ *| Entry Functions | *+-----------------------+*/ void dump_buf(const char *prompt, char *buf, size_t len); int main (int argc, char **argv) { spinor_test(); return 0; } /*+-----------------------+ *| SPI API Functions | *+-----------------------+*/ #define SPI_DEV "/dev/spidev0.0" #define SPI_BITS 8 #define SPI_MODE 0//(SPI_CPHA|SPI_CPOL) #define SPI_SPEED 500000 #define SPI_DUMMY_BYTE 0xA5 void spinor_dev_init(struct spi_info *spi) { uint8_t bits = SPI_BITS; uint32_t speed = SPI_SPEED; uint32_t mode = SPI_MODE; uint32_t request; int ret; spi->hspi = open(SPI_DEV, O_RDWR); if (spi->hspi < 0) { spinor_print("ERROR: open device %s failure: %s\r\n", SPI_DEV, strerror(errno)); return ; } /* * spi mode */ request = mode; if( ioctl(spi->hspi, SPI_IOC_WR_MODE32, &mode) < 0 ) { spinor_print("ERROR: can't set spi mode\n"); return ; } if( ioctl(spi->hspi, SPI_IOC_RD_MODE32, &mode) < 0 ) { spinor_print("ERROR: can't get spi mode\n"); return ; } if (request != mode) { spinor_print("WARNING: device does not support requested mode 0x%x\n", request); } /* * bits per word */ if( ioctl(spi->hspi, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0 ) { spinor_print("ERROR: can't set bits per word"); return ; } if( ioctl(spi->hspi, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0 ) { spinor_print("ERROR: can't get bits per word"); return ; } /* * max speed hz */ if( ioctl(spi->hspi, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0 ) { spinor_print("ERROR: can't set max speed hz"); return ; } if( ioctl(spi->hspi, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0 ) { spinor_print("ERROR: can't get max speed hz"); return ; } printf("spi mode: 0x%x\n", mode); printf("bits per word: %u\n", bits); printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000); } void spi_cs_enable(struct spi_info *spi) { /* * No need set CS in Linux because the device name /dev/spi0.0 * will choose the first slave device, second slave is spi0.1 */ (void)0; } void spi_cs_disable(struct spi_info *spi) { (void)0; } void spi_xcmd(struct spi_info *spi, uint8_t command) { uint8_t rxbyte; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)&command, .rx_buf = (unsigned long)&rxbyte, .len = 1, .delay_usecs = 0, .speed_hz = SPI_SPEED, .bits_per_word = SPI_BITS, }; spi->select(spi); if( ioctl(spi->hspi, SPI_IOC_MESSAGE(1), &tr) < 0 ) { spinor_print("ERROR: can't send spi message:%s\n", strerror(errno)); } spi->deselect(spi); return; } void spi_xfer(struct spi_info *spi, uint8_t *send_buf, uint8_t *recv_buf, int bytes) { struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)send_buf, .rx_buf = (unsigned long)recv_buf, .len = bytes, .delay_usecs = 0, .speed_hz = SPI_SPEED, .bits_per_word = SPI_BITS, }; spi->select(spi); if( ioctl(spi->hspi, SPI_IOC_MESSAGE(1), &tr) < 0 ) { spinor_print("ERROR: can't send spi message:%s\n", strerror(errno)); } spi->deselect(spi); return; } #define SPI_INFO(_hspi, _cs_gpio, _cs_pin) {\ .hspi = _hspi, \ .select = spi_cs_enable, \ .deselect = spi_cs_disable, \ .xcmd = spi_xcmd, \ .xfer = spi_xfer, \ } static struct spi_info spinor_spi = SPI_INFO(-1, W25Q_CS_PORT, W25Q_CS_PIN); /*+-----------------------+ *| W25Q SPI Norflash ID | *+-----------------------+*/ #define W25Q_PAGSIZE 256 /* 1Page=256B */ #define W25Q_SECSIZE 4096 /* 1Sector=16Pages=4KB */ #define W25Q_BLKSIZE 65536 /* 1Block=16Sector=64KB */ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) /* JEDEC ID the 3rd bytes is the storage capacity */ #pragma GCC diagnostic ignored "-Wshift-count-overflow" #define CAPCITY_ID(id) (1UL<<(id&0xFF)) #define NOR_INFO(_name, _jedec_id) \ .name = _name, \ .jedec_id = _jedec_id, \ .block_size = W25Q_BLKSIZE, \ .sector_size= W25Q_SECSIZE, \ .page_size = W25Q_PAGSIZE, \ .capacity = CAPCITY_ID(_jedec_id), \ .n_blocks = CAPCITY_ID(_jedec_id)/W25Q_BLKSIZE, \ .n_sectors = CAPCITY_ID(_jedec_id)/W25Q_SECSIZE, \ .n_pages = CAPCITY_ID(_jedec_id)/W25Q_PAGSIZE, \ static struct flash_info spinor_ids[] = { { NOR_INFO("W25Q512", 0xef4020) }, { NOR_INFO("W25Q256", 0xef4019) }, { NOR_INFO("W25Q128", 0xef4018) }, { NOR_INFO("W25Q64", 0xef4017) }, { NOR_INFO("W25Q32", 0xef4016) }, { NOR_INFO("W25Q16", 0xef4015) }, { NOR_INFO("W25Q80", 0xef4014) }, { NOR_INFO("W25Q40", 0xef4013) }, { NOR_INFO("W25Q20", 0xef4012) }, { NOR_INFO("W25Q10", 0xef4011) }, }; /*+-------------------------------+ *| SPI Norflash HighLevel API | *+-------------------------------+*/ /* SPI Norflash API test function */ void spinor_test(void) { spinor_info_t spinor; int i; uint8_t buf[W25Q_PAGSIZE*2]; if( spinor_init(&spinor) < 0 ) return ; //spinor_erase_chip(&spinor); //spinor_erase_block(&spinor, 1, W25Q_BLKSIZE); spinor_erase_sector(&spinor, 1, W25Q_SECSIZE); memset(buf, 0, sizeof(buf)); spinor_read(&spinor, 0, buf, sizeof(buf)); dump_buf("<<<Read data after erase:\n", (char *)buf, sizeof(buf)); /* Read/Write data test on address not page align */ for(i=0; i<sizeof(buf); i++) buf[i] = i; spinor_write(&spinor, 16, buf, W25Q_PAGSIZE); memset(buf, 0, sizeof(buf)); spinor_read(&spinor, 0, buf, W25Q_PAGSIZE*2); dump_buf("<<<Read data after write:\n", (char *)buf, sizeof(buf)); return ; } /* Initial SPI and detect the flash chip. */ int spinor_init(struct spinor_info *spinor) { spinor->spi = &spinor_spi; spinor_dev_init(spinor->spi); if( !spinor_detect_by_jedec(spinor) ) return -1; printf("Norflash %s detected, capacity %llu KB, %u blocks, %u sectors, %u pages.\r\n", spinor->flash->name, spinor->flash->capacity>>10, spinor->flash->n_blocks, spinor->flash->n_sectors, spinor->flash->n_pages); return 0; } /* Description: Erase whole flash chip. * Reference : P60, 8.2.32 Chip Erase (C7h / 60h) */ int spinor_erase_chip(struct spinor_info *spinor) { struct spi_info *spi = spinor->spi; while (spinor->lock == 1) spinor_Delay(1); spinor->lock = 1; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash EraseChip Begin...\r\n"); #endif spinor_write_enable(spi); spi->xcmd(spi, SPINOR_OP_CHIP_ERASE); spinor_WaitForWriteEnd(spi); #ifdef CONFIG_SPINOR_DEBUG printf("Norflash EraseChip done.\r\n"); #endif spinor_Delay(10); spinor->lock = 0; return 0; } /* Description: Erase blocks by 64KiB, * Reference : P59, 8.2.31 64KB Block Erase with 4-Byte Address (DCh) * @address is the erase start physical address, which can be not block alignment such as 0x10001. * @size is the erase size, which can be larger than a block such as 4097, and it will erase 2 blocks; */ int spinor_erase_block(struct spinor_info *spinor, uint32_t address, uint32_t size) { struct spi_info *spi = spinor->spi; struct flash_info *flash = spinor->flash; uint32_t block, first, last; uint32_t addr; uint8_t buf[5]; int bytes = 0; while (spinor->lock == 1) spinor_Delay(1); spinor->lock = 1; /* find first and last erase block */ first = address / flash->block_size; last = (address+size-1) / flash->block_size; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash Erase %d Bytes Block@0x%x Begin...\r\n", size, address); #endif /* start erase all the blocks */ for( block=first; block<=last; block++) { addr = block * flash->sector_size; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash Erase Block@%x ...\r\n", addr); #endif spinor_WaitForWriteEnd(spi); spinor_write_enable(spi); if (spinor->flash->n_blocks >= 512 ) /* larger than W25Q256 */ { buf[bytes++] = SPINOR_OP_BE_4K_4B; buf[bytes++] = (addr & 0xFF000000) >> 24; } else { buf[bytes++] = SPINOR_OP_BE_4K; } buf[bytes++] = (addr & 0xFF0000) >> 16 ; buf[bytes++] = (addr & 0xFF00) >> 8 ; buf[bytes++] = (addr & 0xFF); spi->xfer(spi, buf, NULL, bytes); spinor_WaitForWriteEnd(spi); } #ifdef CONFIG_SPINOR_DEBUG printf("Norflash EraseBlock@0x%x done.\r\n", address); spinor_Delay(100); #endif spinor_Delay(1); spinor->lock = 0; return 0; } /* Description: Erase sectors by 4KiB * Reference : P56, 8.2.28 Sector Erase with 4-Byte Address (21h) * @address is the erase start physical address, which can be not sector alignment such as 0x1001. * @size is the erase size, which can be larger than a sector such as 4097, and it will erase 2 sectors; */ int spinor_erase_sector(struct spinor_info *spinor, uint32_t address, uint32_t size) { struct spi_info *spi = spinor->spi; struct flash_info *flash = spinor->flash; uint32_t sector, first, last; uint32_t addr; uint8_t buf[5]; int bytes = 0; while (spinor->lock == 1) spinor_Delay(1); spinor->lock = 1; /* find first and last erase sector */ first = address / flash->sector_size; last = (address+size-1) / flash->sector_size; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash Erase %d Bytes Sector@0x%x Begin...\r\n", size, address); #endif /* start erase all the sectors */ for( sector=first; sector<=last; sector++) { addr = sector * flash->sector_size; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash Erase Sector@%x ...\r\n", addr); #endif spinor_WaitForWriteEnd(spi); spinor_write_enable(spi); if (spinor->flash->n_blocks >= 512 ) /* larger than W25Q256 */ { buf[bytes++] = SPINOR_OP_SE_4B; buf[bytes++] = (addr & 0xFF000000) >> 24; } else { buf[bytes++] = SPINOR_OP_SE; } buf[bytes++] = (addr & 0xFF0000) >> 16 ; buf[bytes++] = (addr & 0xFF00) >> 8 ; buf[bytes++] = (addr & 0xFF); spi->xfer(spi, buf, NULL, bytes); spinor_WaitForWriteEnd(spi); } #ifdef CONFIG_SPINOR_DEBUG printf("Norflash EraseSector@0x%x done.\r\n", address); #endif spinor_Delay(1); spinor->lock = 0; return 0; } /* P32: 10.2.14 Page Program (02h) */ int spinor_write(struct spinor_info *spinor, uint32_t address, uint8_t *data, uint32_t size) { struct spi_info *spi = spinor->spi; struct flash_info *flash = spinor->flash; uint32_t page, first, last; uint32_t addr, ofset, len; uint8_t buf[W25Q_PAGSIZE+5]; int bytes = 0; if( address+size > spinor->flash->capacity ) return -1; while (spinor->lock == 1) spinor_Delay(1); spinor->lock = 1; /* find first and last write page */ first = address / flash->page_size; last = (address+size-1) / flash->page_size; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash Write %d Bytes to addr@0x%x Begin...\r\n", size, address); #endif /* address in page and offset in buffer */ addr = address; ofset = 0; /* start write all the pages */ for( page=first; page<=last; page++) { len = flash->page_size - (addr%flash->page_size); len = len > size ? size : len; bytes = 0; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash write addr@0x%x, %u bytes\r\n", addr, len); #endif spinor_WaitForWriteEnd(spi); spinor_write_enable(spi); if (spinor->flash->n_blocks >= 512 ) { buf[bytes++] = SPINOR_OP_PP_4B; buf[bytes++] = (addr & 0xFF000000) >> 24; } else { buf[bytes++] = SPINOR_OP_PP; } buf[bytes++] = (addr & 0xFF0000) >> 16 ; buf[bytes++] = (addr & 0xFF00) >> 8 ; buf[bytes++] = (addr & 0xFF); /* send command and data */ memcpy(&buf[bytes], data+ofset, len); bytes += len; spi->xfer(spi, buf, NULL, bytes); spinor_WaitForWriteEnd(spi); addr += len; ofset += len; size -= len; } #ifdef CONFIG_SPINOR_DEBUG printf("Norflash WriteByte@0x%x done.\r\n", address); #endif spinor_Delay(1); spinor->lock = 0; return 0; } /* Description: The Fast Read instruction can read the entire memory chip. * Reference : P41, 8.2.13 Fast Read with 4-Byte Address (0Ch) * @address is the read start physical address, which can be not page alignment such as 0x101. * @size is the read size, which can be larger than a page such as 257, and it will read 2 pages; */ int spinor_read(struct spinor_info *spinor, uint32_t address, uint8_t *data, uint32_t size) { struct spi_info *spi = spinor->spi; uint8_t buf[W25Q_PAGSIZE+6]; int bytes = 0; int ofset; uint32_t addr = address; if( address+size > spinor->flash->capacity ) return -1; while (spinor->lock == 1) spinor_Delay(1); spinor->lock = 1; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash Read %d Bytes from addr@0x%x Begin...\r\n", size, address); #endif while( size > 0 ) { bytes = size>W25Q_PAGSIZE ? W25Q_PAGSIZE : size; memset(buf, SPI_DUMMY_BYTE, sizeof(buf)); ofset = 0; #ifdef CONFIG_SPINOR_DEBUG printf("Norflash read addr@0x%x, %d bytes\r\n", addr, bytes); #endif /* send instruction and address */ if (spinor->flash->n_blocks >= 512 ) { buf[ofset++] = SPINOR_OP_READ_FAST_4B; buf[ofset++] = (addr & 0xFF000000) >> 24; } else { buf[ofset++] = SPINOR_OP_READ_FAST; } buf[ofset++] = (addr & 0xFF0000) >> 16 ; buf[ofset++] = (addr & 0xFF00) >> 8 ; buf[ofset++] = (addr & 0xFF); ofset += 1; /* Skip first dummy byte */ /* Send command and read data out */ spi->xfer(spi, buf, buf, ofset+bytes); memcpy(data, &buf[ofset], bytes); size -= bytes; addr += bytes; data += bytes; } #ifdef CONFIG_SPINOR_DEBUG printf("Norflash ReadBytes@0x%x done.\r\n", address); #endif spinor->lock = 0; return 0; } /*+-------------------------------+ *| SPI Norflash LowLevel API | *+-------------------------------+*/ /* Detect the norflash by JEDEC ID */ int spinor_detect_by_jedec(struct spinor_info *spinor) { uint32_t jedec_id; int i, found = 0; jedec_id = spinor_read_jedecid(spinor->spi); for(i=0; i<ARRAY_SIZE(spinor_ids); i++) { if(spinor_ids[i].jedec_id == jedec_id) { found = 1; spinor->flash = &spinor_ids[i]; break; } } printf("Detect JEDEC ID[0x%x], Norflash %s found\r\n", jedec_id, found?spinor->flash->name:"not"); return found; } /* Description: Read the chipset UNIQUE ID. * Reference : P68, 8.2.40 Read Unique ID Number (4Bh) */ int spinor_read_uniqid(struct spi_info *spi, uint8_t *uniq_id) { uint8_t i; uint8_t buf[13]; /* Instruction(1B) + Dummy(4B) + UID(8B)*/ if( !uniq_id ) return -1; buf[0] = SPINOR_OP_RDUID; spi->xfer(spi, buf, buf, sizeof(buf)); /* Skip 4 bytes dummy bytes */ for (i=0; i<8; i++) { uniq_id[i] = buf[5+i]; } return 0; } /* Description: Read the chipset JEDEC ID. * Reference : P69, 8.2.41 Read JEDEC ID (9Fh) */ uint32_t spinor_read_jedecid(struct spi_info *spi) { uint32_t jedec_id = 0x0; uint8_t buf[4]; buf[0] = SPINOR_OP_RDID; spi->xfer(spi, buf, buf, sizeof(buf)); jedec_id = (buf[1] << 16) | (buf[2] << 8) | buf[3]; return jedec_id; } /* Description: Write Enable * Reference : P31, 8.2.1 Write Enable (06h) */ void spinor_write_enable(struct spi_info *spi) { spi->xcmd(spi, SPINOR_OP_WREN); spinor_Delay(1); } /* Description: Write Disable * Reference : P32, 8.2.3 Write Disable (04h) */ void spinor_write_disable(struct spi_info *spi) { spi->xcmd(spi, SPINOR_OP_WRDI); spinor_Delay(1); } /* Description: Read Status Register * Reference : P32, 8.2.4 Read Status Register-1 (05h), Status Register-2 (35h) & Status Register-3 (15h) */ uint8_t spinor_read_status_reg(struct spi_info *spi, uint8_t reg) { uint8_t cmd[REG_STATUS_MAX] = { SPINOR_OP_RDSR1 , SPINOR_OP_RDSR2, SPINOR_OP_RDSR3 }; /* Status Register 1~3 */ uint8_t buf[2]; if( reg>= REG_STATUS_MAX ) return 0xFF; buf[0] = cmd[reg]; buf[1] = SPI_DUMMY_BYTE; spi->xfer(spi, buf, buf, sizeof(buf)); return buf[1]; } /* Description: Write Status Register * Reference : P33, 8.2.5 Write Status Register-1 (01h), Status Register-2 (31h) & Status Register-3 (11h) */ void spinor_write_status_reg(struct spi_info *spi, uint8_t reg, uint8_t value) { uint8_t cmd[REG_STATUS_MAX] = { SPINOR_OP_WRSR1 , SPINOR_OP_WRSR2, SPINOR_OP_WRSR3 }; /* Status Register 1~3 */ uint8_t buf[2]; if( reg>= REG_STATUS_MAX ) return ; buf[0] = cmd[reg]; buf[1] = value; spi->xfer(spi, buf, buf, sizeof(buf)); } /* Description: Wait flash program/erase finished by read Status Register for BUSY bit * Reference : P15, 7.1 Status Registers */ void spinor_WaitForWriteEnd(struct spi_info *spi) { uint8_t buf[2]; spinor_Delay(1); do { buf[0] = SPINOR_OP_RDSR1; buf[1] = SPI_DUMMY_BYTE; spi->xfer(spi, buf, buf, sizeof(buf)); spinor_Delay(1); } while ((buf[1] & 0x01) == 0x01); } /*+----------------+ *| dump_buf | *+----------------+*/ void print_buf(const char *prompt, uint8_t *buf, int size) { int i; if( !buf ) { return ; } if( prompt ) { printf("%-32s ", prompt); } for(i=0; i<size; i++) { printf("%02X ", buf[i]); } printf("\r\n"); return ; } #define LINELEN 81 #define CHARS_PER_LINE 16 static char *print_char = " " " " " !\"#$%&'()*+,-./" "0123456789:;<=>?" "@ABCDEFGHIJKLMNO" "PQRSTUVWXYZ[\\]^_" "`abcdefghijklmno" "pqrstuvwxyz{|}~ " " " " " " ???????????????" "????????????????" "????????????????" "????????????????" "????????????????" "????????????????"; void dump_buf(const char *prompt, char *buf, size_t len) { int rc; int idx; char prn[LINELEN]; char lit[CHARS_PER_LINE + 2]; char hc[4]; short line_done = 1; if( prompt ) printf("%s", prompt); rc = len; idx = 0; lit[CHARS_PER_LINE] = '\0'; while (rc > 0) { if (line_done) snprintf(prn, LINELEN, "%08X: ", idx); do { unsigned char c = buf[idx]; snprintf(hc, 4, "%02X ", c); strncat(prn, hc, LINELEN); lit[idx % CHARS_PER_LINE] = print_char[c]; } while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); line_done = (idx % CHARS_PER_LINE) == 0; if (line_done) { printf("%s %s\r\n", prn, lit); } } if (!line_done) { int ldx = idx % CHARS_PER_LINE; lit[ldx++] = print_char[(int)buf[idx]]; lit[ldx] = '\0'; while ((++idx % CHARS_PER_LINE) != 0) strncat(prn, " ", sizeof(prn)-strlen(prn)); printf("%s %s\r\n", prn, lit); } } modules/w25qflash.h
New file @@ -0,0 +1,175 @@ /********************************************************************************* * Copyright: (C) 2023 LingYun IoT System Studio. All Rights Reserved. * Author: Guo Wenxue <guowenxue@gmail.com> * * Description: This file is W25Qxx SPI Norflash driver on RaspberryPi 40Pin. * * W25QXX RaspberryPi 40Pin * VCC <---> 3.3V(Pin#1) * CS <---> CS(Pin#24) * DO <---> MISO(Pin#21) * GND <---> GND(Pin#9) * CLK <---> SCLK(Pin#23) * DI <---> MOSI(Pin#19) * ********************************************************************************/ #ifndef _W25QFLASH_H #define _W25QFLASH_H #include <stdbool.h> #define _W25QXX_DEBUG 1 /* Flash opcodes. Refer to <<W25Q256JV.pdf>> P26 Table 8.1.2 Instruction Set Table */ #define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ #define SPINOR_OP_RDUID 0x4b /* Read unique ID */ #define SPINOR_OP_WRSR1 0x01 /* Write status register-1 */ #define SPINOR_OP_WRSR2 0x31 /* Write status register-2 */ #define SPINOR_OP_WRSR3 0x11 /* Write status register-3 */ #define SPINOR_OP_BP 0x02 /* Byte program */ #define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ #define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_RDSR1 0x05 /* Read status register-1 */ #define SPINOR_OP_RDSR2 0x35 /* Read status register-2 */ #define SPINOR_OP_RDSR3 0x15 /* Read status register-3 */ #define SPINOR_OP_WREN 0x06 /* Write enable */ #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ #define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */ #define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ #define SPINOR_OP_BE_4K_4B 0xdc /* Block erase (64KiB) with 4-Byte Address */ #define SPINOR_OP_BE_4K 0xd8 /* Block erase (64KiB) */ #define SPINOR_OP_SE_4B 0x21 /* Sector erase (4KiB) with 4-Byte Address */ #define SPINOR_OP_SE 0x20 /* Sector erase (4KiB) */ #define SPINOR_OP_PP_4B 0x12 /* Page Program (up to 256 bytes) with 4-Byte Address */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ #define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */ #define SPINOR_OP_SRST 0x99 /* Software Reset */ typedef struct spi_info { int hspi; /* SPI device description */ void (*select)(struct spi_info *spi); /* CS enable function */ void (*deselect)(struct spi_info *spi); /* CS disable function */ void (*xcmd)(struct spi_info *spi, uint8_t command); /* Send a byte command */ void (*xfer)(struct spi_info *spi, uint8_t *send_buf, uint8_t *recv_buf, int bytes); /* Transmit and Receive N byte */ } spi_info_t; typedef struct flash_info { char *name; /* Chip name */ uint32_t jedec_id; /* JEDEC ID, 3 bytes */ uint64_t capacity; /* Chip size in bytes */ uint32_t block_size; /* Block size in bytes */ uint32_t sector_size; /* Sector size in bytes */ uint32_t page_size; /* Page size in bytes */ uint32_t n_blocks; /* Number of blocks */ uint32_t n_sectors; /* Number of sectors */ uint32_t n_pages; /* Number of pages */ } flash_info_t; typedef struct spinor_info { spi_info_t *spi; flash_info_t *flash; uint8_t lock; } spinor_info_t; /* Status registers */ enum { REG_STATUS1, REG_STATUS2, REG_STATUS3, REG_STATUS_MAX, }; /*+-------------------------------+ *| SPI Norflash HighLevel API | *+-------------------------------+*/ /* SPI Norflash API test function */ extern void spinor_test(void); /* Initial SPI and detect the flash chip */ extern int spinor_init(struct spinor_info *spinor); /* Description: Erase whole flash chip. * Reference : P60, 8.2.32 Chip Erase (C7h / 60h) */ extern int spinor_erase_chip(struct spinor_info *spinor); /* Description: Erase blocks by 64KiB, * Reference : P59, 8.2.31 64KB Block Erase with 4-Byte Address (DCh) * @address is the erase start physical address, which can be not block alignment such as 0x10001. * @size is the erase size, which can be larger than a block such as 4097, and it will erase 2 blocks; */ extern int spinor_erase_block(struct spinor_info *spinor, uint32_t address, uint32_t size); /* Description: Erase sectors by 4KiB * Reference : P56, 8.2.28 Sector Erase with 4-Byte Address (21h) * @address is the erase start physical address, which can be not sector alignment such as 0x1001. * @size is the erase size, which can be larger than a sector such as 4097, and it will erase 2 sectors; */ extern int spinor_erase_sector(struct spinor_info *spinor, uint32_t address, uint32_t size); /* Description: Page random write by 256B * @addr is the write start physical address, which can be not page alignment such as 0x101. * @size is the write size, which can be larger than a page such as 257, and it will write 2 pages; */ extern int spinor_write(struct spinor_info *spinor, uint32_t address, uint8_t *data, uint32_t bytes); /* Description: The Fast Read instruction can read the entire memory chip. * Reference : P41, 8.2.13 Fast Read with 4-Byte Address (0Ch) * @address is the read start physical address, which can be not page alignment such as 0x101. * @size is the read size, which can be larger than a page such as 257, and it will read 2 pages; */ extern int spinor_read(struct spinor_info *spinor, uint32_t address, uint8_t *buf, uint32_t bytes); /*+-------------------------------+ *| SPI Norflash LowLevel API | *+-------------------------------+*/ /* Detect the norflash by JEDEC ID */ int spinor_detect_by_jedec(struct spinor_info *spinor); /* Description: Read the chipset UNIQUE ID. * Reference : P68, 8.2.40 Read Unique ID Number (4Bh) */ int spinor_read_uniqid(struct spi_info *spi, uint8_t *uniq_id); /* Description: Read the chipset JEDEC ID. * Reference : P69, 8.2.41 Read JEDEC ID (9Fh) */ uint32_t spinor_read_jedecid(struct spi_info *spi); /* Description: Write Enable * Reference : P31, 8.2.1 Write Enable (06h) */ void spinor_write_enable(struct spi_info *spi); /* Description: Write Disable * Reference : P32, 8.2.3 Write Disable (04h) */ void spinor_write_disable(struct spi_info *spi); /* Description: Read Status Register * Reference : P32, 8.2.4 Read Status Register-1 (05h), Status Register-2 (35h) & Status Register-3 (15h) */ uint8_t spinor_read_status_reg(struct spi_info *spi, uint8_t reg); /* Description: Write Status Register * Reference : P33, 8.2.5 Write Status Register-1 (01h), Status Register-2 (31h) & Status Register-3 (11h) */ void spinor_write_status_reg(struct spi_info *spi, uint8_t reg, uint8_t value); /* Description: Wait flash program/erase finished by read Status Register for BUSY bit * Reference : P15, 7.1 Status Registers */ void spinor_WaitForWriteEnd(struct spi_info *spi); #endif