{
int i = 0;
/* Get the font */
const unsigned char *dots = oled_asc2_8x16[c - ' '];
/* Send to OLED */
OLEDSetPos(page, col);
/* Send 8 bytes of data */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i]);
OLEDSetPos(page+1, col);
/* Send 8 bytes of data */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i+8]);
}
To display a character, first get the font data, then send out 8 bytes of data, and then send out 8 bytes of data in a new line.
Next, we will implement the OLED coordinate position setting function. First, we will set the page:
D0~D2 represent page data, D3-D7 are fixed values, so the command content of each write is 0xB0+page;
Then set the columns:
Send twice, showing sending the low byte 4 bits, and then sending the high byte 4 bits;
static void OLEDSetPos(int page, int col)
{
OLEDWriteCmd(0xB0 + page); /* page address */
OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */
OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */
}
As mentioned above, the OLED master controller has three address modes. We usually use the page addressing mode (A[1:0]=10xb). Although this is the default setting, it is still better to set it:
That is, send 0x20 first, and then set A[1:0]=10:
static void OLEDSetPageAddrMode(void)
{
OLEDWriteCmd(0x20);
OLEDWriteCmd(0x02);
}
In the display, a screen clearing function is generally required to clear the currently displayed data. The screen clearing function is relatively simple, just write 0 to all positions:
static void OLEDClear(void)
{
int page, i;
for (page = 0; page < 8; page ++)
{
OLEDSetPos(page, 0);
for (i = 0; i < 128; i++)
OLEDWriteDat(0);
}
}
Then put the address mode OLEDSetPageAddrMode() and the screen clearing function OLEDClear() in SPI_GPIO_Init(), and add gpio_spi.o and oled.o to the Makefile.
Finally, add the initialization and display functions to the main function:
SPIInit();
OLEDInit();
OLEDPrint(0,0,"www.100ask.net, 100ask.taobao.com");
Section 003 SPI_FLASH Programming Read ID
This section explains how to use SPI to operate Flash. We modify the code from the previous lesson and add a file spi_flash.c and its header file spi_flash.h.
Let's first do the simplest SPI operation to read the Flash ID, SPIFlashID().
The Flash ID includes the manufacturer ID and the device ID, which are saved in pMID and pDID respectively.
According to the Flash chip manual W25Q16DV.pdf, we know that we need to issue a command 0x90 first, then send the 24-bit address 0, and then read the data. The first 8 bits are the device ID, and then the 8-bit device ID. Before the operation, you must select the SPI Flash, and release the SPI Flash after the chip selection:
void SPIFlashReadID(int *pMID, int *pDID)
{
SPIFlash_Set_CS(0); /* Select SPI FLASH */
SPISendByte(0x90);
SPIFlashSendAddr(0);
*pMID = SPIRecvByte();
*pDID = SPIRecvByte();
SPIFlash_Set_CS(1);
}
The send 24 address is encapsulated into a function SPIFlashSendAddr():
static void SPIFlashSendAddr(unsigned int addr)
{
SPISendByte(addr >> 16);
SPISendByte(addr >> 8);
SPISendByte(addr & 0xff);
}
Complete the above sub-functions in sequence, starting with the SPI chip select. From the schematic diagram in the previous section, you can see that the SPI Flash chip select is GPG2:
static void SPIFlash_Set_CS(char val)
{
if (val)
GPGDAT |= (1<<2);
else
GPGDAT &= ~(1<<2);
}
SPISendByte() is the same as the previous OLED, so there is no need to write it, so only SPIRecvByte() is left, which is implemented in gpio_spi.c:
unsigned char SPIRecvByte(void)
{
int i;
unsigned char val = 0;
for (i = 0; i < 8; i++)
{
val <<= 1;
SPI_Set_CLK(0);
if (SPI_Get_DI())
val |= 1;
SPI_Set_CLK(1);
}
return val;
}
Read the value on the DI pin at each clock cycle, which is the MISO pin for the SOC:
static char SPI_Get_DI(void)
{
if (GPGDAT & (1<<5))
return 1;
else
return 0;
}
So far, reading the Flash ID is basically realized. Finally, the print function is called in the main function and displayed on the serial port and OLED respectively:
SPIFlashReadID(&mid, &pid);
printf("SPI Flash : MID = 0x%02x, PID = 0x%02xnr", mid, pid);
sprintf(str, "SPI : %02x, %02x", mid, pid);
OLEDPrint(4,0,str);
Remember to add the newly generated spi_flash.o to the Makefile.
Section 004 SPI_FLASH Programming Reading and Writing
As a storage chip, the most important thing about Flash is to store and read the stored data. In this section, we will implement the reading and writing of data in Flash.
For Flash, each write operation requires the following steps:
Unprotect (write enable, write status register);
Erase (write enable)
Write data (write enable)
It can be seen that for write operations, write enable is required every time. By consulting the chip manual, it can be seen that write enable is relatively simple, and only needs to send 0x06 command:
On the contrary, write protection is written to 0x04:
static void SPIFlashWriteEnable(int enable)
{
if (enable)
{
SPIFlash_Set_CS(0);
SPISendByte(0x06);
SPIFlash_Set_CS(1);
}
else
{
SPIFlash_Set_CS(0);
SPISendByte(0x04);
SPIFlash_Set_CS(1);
}
}
Then read and write the status register. There are two status registers. Read status register 1 through 0x05 and read status register 2 through 0x35:
static unsigned char SPIFlashReadStatusReg1(void)
{
unsigned char val;
SPIFlash_Set_CS(0);
SPISendByte(0x05);
val = SPIRecvByte();
SPIFlash_Set_CS(1);
return val;
}
static unsigned char SPIFlashReadStatusReg2(void)
{
unsigned char val;
SPIFlash_Set_CS(0);
SPISendByte(0x35);
val = SPIRecvByte();
SPIFlash_Set_CS(1);
return val;
}
To write the status register, first send the 0x01 command, then send status register 1 and status register 2 in sequence:
static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{
SPIFlashWriteEnable(1);
SPIFlash_Set_CS(0);
SPISendByte(0x01);
SPISendByte(reg1);
SPISendByte(reg2);
SPIFlash_Set_CS(1);
SPIFlashWaitWhenBusy();
}
static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{
SPIFlashWriteEnable(1);
SPIFlash_Set_CS(0);
SPISendByte(0x01);
SPISendByte(reg1);
SPISendByte(reg2);
SPIFlash_Set_CS(1);
SPIFlashWaitWhenBusy();
}
Writing the status register also needs to be unprotected. By default, SPIFlashWriteEnable()
the status register can be written after it is issued, but in order to ensure that there is no risk, SRP1 and SRP2 are manually set to 0, that is, the highest bit of status register 1 is cleared to zero and the lowest bit of status register is cleared to zero:
static void SPIFlashClearProtectForStatusReg(void)
{
unsigned char reg1, reg2;
reg1 = SPIFlashReadStatusReg1();
reg2 = SPIFlashReadStatusReg2();
reg1 &= ~(1<<7);
reg2 &= ~(1<<0);
SPIFlashWriteStatusReg(reg1, reg2);
}
Flash has two protection mechanisms, one is to protect the status register, the other is to protect the stored data. Now let's clear the data protection.
You need to set CMP to 0 and set BP0, BP1, and BP2 to 0:
static void SPIFlashClearProtectForData(void)
{
/* cmp=0,bp2,1,0=0b000 */
unsigned char reg1, reg2;
reg1 = SPIFlashReadStatusReg1();
reg2 = SPIFlashReadStatusReg2();
reg1 &= ~(7<<2);
reg2 &= ~(1<<6);
SPIFlashWriteStatusReg(reg1, reg2);
}
```
Put both clear write protection functions together as a SPI Flash initialization function:
```
void SPIFlashInit(void)
{
SPIFlashClearProtectForStatusReg();
SPIFlashClearProtectForData();
}
To implement erasure, the erase command needs to first send a 0x20 command, and then send the 24-bit address of the location to be erased:
/* erase 4K */
void SPIFlashEraseSector(unsigned int addr)
{
SPIFlashWriteEnable(1);
SPIFlash_Set_CS(0);
SPISendByte(0x20);
SPIFlashSendAddr(addr);
SPIFlash_Set_CS(1);
SPIFlashWaitWhenBusy();
}
To ensure that the erase is successful, you need to read the first bit of status register 1:
static void SPIFlashWaitWhenBusy(void)
{
while (SPIFlashReadStatusReg1() & 1);
}
Then comes the programming function, which first sends the command 0x02, then the 24-bit address, and finally sends the data one by one:
/* program */
void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len)
{
int i;
SPIFlashWriteEnable(1);
SPIFlash_Set_CS(0);
SPISendByte(0x02);
SPIFlashSendAddr(addr);
for (i = 0; i < len; i++)
SPISendByte(buf[i]);
SPIFlash_Set_CS(1);
SPIFlashWaitWhenBusy();
}
Like the previous erase operation, the programming operation is not necessarily real-time, and the status flag needs to be read to determine whether it is completed.
The read function also operates in a similar way. First, send the command 0x03, then send the 24-bit address, and then read the data one by one:
void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
{
int i;
SPIFlash_Set_CS(0);
SPISendByte(0x03);
SPIFlashSendAddr(addr);
for (i = 0; i < len; i++)
buf[i] = SPIRecvByte();
SPIFlash_Set_CS(1);
}
At this point, the basic Flash read and write functions have been completed. The main function calls the erase function to erase the data in sector 4096, then writes a string to address 4096, reads it from the address, and prints it out on the serial port and OLED:
SPIFlashEraseSector(4096);
SPIFlashProgram(4096, "100ask", 7);
SPIFlashRead(4096, str, 7);
printf("SPI Flash read from 4096: %snr", str);
OLEDPrint(4,0,str);
Section 005_Displaying ADC value on OLED
In this section, we display the ADC voltage value on the OLED and adjust the adjustable resistor to make the ADC value change continuously on the screen.
There is an adc_ts touch screen program in the hardware of the JZ2440 main CD. Extract the adc_ts.c and adc_ts.h files and put them in the code to be written in this section of the video.
Previous article:Lesson 019 I2C protocol detailed explanation and bare metal program analysis
Next article:ARM's PWM module pulse width modulation and ultrasonic system design
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Working hours of heart rate bracelet
- A great popular science article on electromagnetic compatibility principles, methods and design!
- [GD32L233C] + 1. Unboxing & Development Environment Setup
- What is it like to run high-speed lines on the signal layer of ultra-thick copper?
- ADC conversion method and main parameters
- GaN: Key Technologies for Solving 5G Challenges
- MicroPython driver porting for LIS2DW12 motion sensor
- The 3.3V square wave signal output by the microcontroller can be measured by the multimeter as 1.5V voltage
- What is the relationship between the machine cycle and clock cycle of RH850?
- The highest increase is 30%, 10 IC manufacturers including ST, MediaTek, and ON Semiconductor have increased prices!