[Lanqiao Cup] [Eingebettete Kategorie] Abschnitt 8: EEPROM


IIC-Protokoll:
PCF8591: Integrierte AD/DA-Funktion. Dieser Chip kommuniziert über das IIC-Protokoll mit dem Mikrocontroller.
AT24C02: Der EEPROM-Chip kommuniziert auch über das IIC-Protokoll mit dem Mikrocontroller.

Einführung in das IIC-Protokoll:

Der I2C-Bus ist eine standardmäßige bidirektionale Schnittstelle, bei der der Controller/Prozessor als Master fungiert und mit Slave-Geräten kommuniziert. Ein Slave-Gerät kann nur dann aktiv Daten senden, wenn es vom Master-Gerät angesprochen wurde. Jedes Gerät am I2C-Bus verfügt über eine spezifische Geräteadresse , um mehrere Geräte am selben I2C-Bus zu unterscheiden. Viele Slave-Geräte müssen beim Booten konfiguriert werden, um das Verhalten des Geräts festzulegen. Dies geschieht normalerweise, wenn der Master mit einer eindeutigen Registeradresse auf die interne Registerzuordnung des Slaves zugreift. Ein Gerät kann über ein oder mehrere Register verfügen, die zum Speichern, Schreiben oder Lesen von Daten verwendet werden.
Die physische I2C-Schnittstelle besteht aus seriellen Taktleitungen (SCL) und seriellen Datenleitungen (SDA). Sowohl die SDA- als auch die SCL-Leitung müssen über Pull-up-Widerstände mit VCC verbunden sein. Die Größe des Pull-up-Widerstands wird durch die Kapazität auf dem I2C-Bus bestimmt (weitere Einzelheiten finden Sie im Artikel zur Berechnung des I2C-Pull-up-Widerstands ([SLVA689]). Die Datenübertragung kann nur initiiert werden, wenn der Bus aktiviert ist Leerlauf. Wenn nach einer STOP-Bedingung sowohl die SDA- als auch die SCL-Leitung hoch sind, gilt der Bus als inaktiv.

Der allgemeine Prozess des Zugriffs des Master-Geräts auf das Slave-Gerät

Angenommen, das Master-Gerät möchte Daten an das Slave-Gerät senden:

  1. Der Master-Sender sendet eine START-Bedingung und spricht den Slave-Empfänger an
  2. Der Master-Sender sendet Daten an den Slave-Empfänger
  3. Der Master-Sender beendet die Übertragung mit einer STOP-Bedingung

Wenn der Master Daten vom Slave empfangen/lesen möchte:

  1. Der Master-Sender sendet eine START-Bedingung und spricht den Slave-Empfänger an
  2. Der Master-Empfänger sendet eine Anfrage zum Lesen des Registers
  3. Der Master-Empfänger empfängt Daten vom Slave-Sender
  4. Der Master-Sender beendet die Übertragung mit einer STOP-Bedingung

START- und STOP-Bedingungen

Die I2C-Kommunikation mit dem Master-Gerät wird durch das Senden einer START-Bedingung durch das Master-Gerät initiiert und durch das Senden einer STOP-Bedingung durch das Master-Gerät beendet. Ein High-Low-Übergang auf der SDA-Leitung, wenn SCL High ist, definiert eine START-Bedingung. Ein Übergang von niedrig nach hoch auf der SDA-Leitung, während SCL hoch ist, definiert einen STOP-Zustand.

Die wiederholte START-Bedingung ähnelt der START-Bedingung und wird verwendet, um die Übertragung neu zu starten, ohne den Bus in den Leerlauf zu versetzen. Sie sieht genauso aus wie die START-Bedingung, unterscheidet sich jedoch von der START-Bedingung, da sie vor der STOP-Bedingung auftritt (wenn der Bus nicht im Leerlauf ist). Dies ist nützlich, wenn der Master eine neue Kommunikation starten möchte, aber nicht möchte, dass der Bus während eines STOP-Zustands im Leerlauf ist, was andernfalls dazu führen könnte, dass der Master die Kontrolle über ein anderes Gerät verliert (in einer Multi-Master-Umgebung).

Datengültigkeit und Byteformat

Während jedes Taktimpulses von SCL wird ein Datenbit übertragen. Ein Byte besteht aus 8 Bits auf der SDA-Leitung . Bytes können Geräteadressen, Registeradressen oder Daten sein, die auf ein Slave-Gerät geschrieben oder von diesem gelesen werden. Die Daten werden zuerst mit dem höchstwertigen Bit (MSB) übertragen . Zwischen START- und STOP-Bedingungen können beliebig viele Datenbytes vom Master zum Slave übertragen werden. Die Daten auf der SDA-Leitung müssen während der High-Periode des Taktzyklus stabil bleiben, da Änderungen auf der Datenleitung, wenn SCL High ist, als Steuerbefehle (START oder STOP) interpretiert werden.

Bestätigung (ACK) und Nichtbestätigung (NACK)

Auf jedes Datenbyte (einschließlich des Adressbytes) folgt ein ACK-Bit vom Empfänger. Das ACK-Bit zeigt an, dass der Empfänger dem Sender mitgeteilt hat, dass das Byte erfolgreich empfangen wurde und ein weiteres Byte gesendet werden kann.
Der Sender muss die SDA-Leitung freigeben, bevor der Empfänger eine Bestätigung sendet. Um das ACK-Bit zu senden, sollte der Empfänger die SDA-Leitung während der Low-Phase des ACK/NACK-bezogenen Taktzyklus (Zyklus 9) herunterziehen, damit die SDA-Leitung während der High-Phase von ACK stabil niedrig ist.
Wenn die SDA-Leitung während des ACK/NACK-bezogenen Taktzyklus hoch bleibt, wird dies als NACK interpretiert. Es gibt mehrere Bedingungen, die dazu führen können, dass ein NACK generiert wird:

  1. Der kommunizierende Teilnehmer kann weder empfangen noch senden, da er eine Echtzeitfunktion ausführt und noch nicht bereit ist, mit dem Master zu kommunizieren.
  2. Bei der Übertragung erhält der Empfänger Daten oder Befehle, die er nicht versteht.
  3. Während der Übertragung kann der Empfänger keine weiteren Datenbytes empfangen.
  4. Der Master-Receiver beendet das Lesen der Daten und teilt sie dem Slave-Gerät per NACK mit.

IIC-Datenübertragung

Daten müssen an das Slave-Gerät gesendet oder von diesem empfangen werden. Dies geschieht jedoch durch Lesen oder Schreiben aus einem Register im oder vom Slave-Gerät.
Ein Register ist ein Ort im Speicher des Slaves, der Informationen enthält, seien es Konfigurationsinformationen oder einige abgetastete Daten, die an den Master zurückgesendet werden sollen. Das Master-Gerät muss Informationen in diese Register schreiben, um das Slave-Gerät anzuweisen, Aufgaben auszuführen.
Obwohl in I2C-Slave-Geräten normalerweise Register vorhanden sind, beachten Sie bitte, dass nicht alle Slave-Geräte über Register verfügen. Einige Geräte sind einfach und enthalten nur ein Register, das direkt geschrieben werden kann, indem die Registerdaten unmittelbar nach der Slave-Adresse gesendet werden, anstatt das Register zu adressieren.
Ein Beispiel für ein Single-Register-Gerät ist ein 8-Bit-I2C-Schalter, der über I2C-Befehle gesteuert wird.
Da es 1 Bit zum Aktivieren oder Deaktivieren des Kanals gibt, ist nur 1 Register erforderlich und der Master schreibt die Registerdaten nur nach der Slave-Adresse und überspringt die Registernummer.

Daten auf Slave-Gerät schreiben

Um auf den I2C-Bus zu schreiben, sendet der Master eine START-Bedingung auf den Bus, wobei die Slave-Adresse und das letzte Bit auf 0 (R/W-Bit) gesetzt sind, was einen Schreibvorgang anzeigt. Nachdem der Slave das Bestätigungsbit gesendet hat, sendet der Master die Registeradresse des Registers, in das er schreiben möchte. Der Slave bestätigt erneut, um dem Master mitzuteilen, dass er bereit ist. Danach beginnt der Master, Daten an die Register des Slaves zu senden. Bis der Master alle benötigten Daten gesendet hat (manchmal ist dies nur ein Byte), beendet der Master die Übertragung mit einer STOP-Bedingung.

Daten vom Slave lesen

Das Lesen von einem Slave ist dem Schreiben sehr ähnlich, erfordert jedoch einige zusätzliche Schritte. Um von einem Slave zu lesen, muss der Master dem Slave zunächst mitteilen, aus welchem ​​Register er lesen möchte. Dies wird vom Master auf ähnliche Weise wie ein Schreibvorgang initiiert, indem er die Adresse mit dem R/W-Bit gleich 0 (was einen Schreibvorgang anzeigt) sendet, gefolgt von der Adresse des Registers, aus dem er lesen möchte. Sobald der Slave diese Registeradresse bestätigt, sendet der Master erneut die START-Bedingung, gefolgt von der Slave-Adresse, wobei das R/W-Bit auf 1 gesetzt ist (was einen Lesevorgang anzeigt). Diesmal bestätigt der Slave die Leseanforderung und der Master gibt den SDA-Bus frei, taktet den Slave jedoch weiterhin. Während dieses Teils der Kommunikation wird das Master-Gerät zum Master-Empfänger und das Slave-Gerät zum Slave-Sender.
Der Master sendet weiterhin Taktimpulse, gibt jedoch die SDA-Leitung frei, damit der Slave Daten übertragen kann. Am Ende jedes Datenbytes sendet der Master eine Bestätigung an den Slave, um ihm mitzuteilen, dass er zum Empfang weiterer Daten bereit ist. Sobald der Master die erwartete Anzahl an Bytes empfangen hat, sendet er ein NACK und signalisiert dem Slave, die Kommunikation zu stoppen und den Bus freizugeben. Der Host beendet dann die Kommunikation mit einer STOP-Bedingung.

AT24C02-Chip (EEPROM)

Das Hauptmerkmal besteht darin, dass es beim Ausschalten der Stromversorgung nicht verloren geht und die interne Speicherstruktur eine MOS-Röhre ist. (Flash ist auch eine Speicherstruktur, die bei Stromausfall nicht verloren geht.)
Das Schaltbild sieht wie folgt aus:
Fügen Sie hier eine Bildbeschreibung ein
Pin 8 ist VCC und Pin 4 ist GND, der Pin für die Stromversorgung. Nach dem Anschließen kann der Chip funktionieren .
Dann gibt es noch die Kommunikationspins des IIC. SCL und SDA sind mit dem P20-Port bzw. P21-Port des Mikrocontrollers verbunden. Über zwei Pull-up-Widerstände auf VCC hochziehen, da das IIC-Slave-Gerät und der Host über den Open-Drain-Modus kommunizieren. Wenn es sich um Open-Drain handelt, muss der VCC des Pull-up-Widerstands verwendet werden, um einen hohen Pegel auszugeben.

Wozu dienen also die Pins E1, E2 und E3?
Fügen Sie hier eine Bildbeschreibung ein
IIC-Geräte haben eine Geräteadresse. Schauen Sie sich für 24C02 einfach die erste Zeile des Bildes oben an, da die Kapazität 1 KB/2 KB beträgt.
Es ist ersichtlich, dass 4 der ersten 7 Ziffern bereits festgelegt sind und die verbleibenden drei Ziffern im Schaltplan A0, A1 und A2 sind. Dann werden im Schaltplan A0, A1 und A2 alle mit GND verbunden und dann die Adresse bestimmt. Die ersten sieben Ziffern lauten 1010000.
Die letzte Ziffer bestimmt, ob geschrieben oder gelesen wird. Wenn es 0 ist, bedeutet dies, dass auf den Chip geschrieben wird, wenn es 1 ist, bedeutet es, dass es gelesen wird.
Also die Geräteadresse von AT24C02: 1010000 (R/W (Schreiben ist aktiv niedrig))

  1. 0xA0 steht für das Schreiben von Daten in AT24C02
  2. 0xA1 steht für das Lesen der Daten von AT24C02

Der WC-Pin ist ein Schreibschutz-Pin. Wenn WP hoch ist, kann AT24C02 keine Daten schreiben.

AT24C02
– Niederspannungs- und Standardspannungsbetrieb
– 2,7 (VCC = 2,7 V bis 5,5 V)
– 1,8 (VCC = 1,8 V bis 5,5 V)
• Intern organisiert 128 x 8 (1 K), 256 x 8 (2 K) , 512 x 8 (4K),
1024 x 8 (8K) oder 2048 x 8 (16K)
• Zweidrahtige serielle Schnittstelle
• Schmitt-Trigger, gefilterte Eingänge zur Rauschunterdrückung
• Bidirektionales Datenübertragungsprotokoll
• 100 kHz (1,8 V) und 400 kHz (2,7 V, 5 V) Kompatibilität
• Schreibschutz-Pin für Hardware-Datenschutz
• 8-Byte-Seite (1K, 2K), 16-Byte-Seite (4K, 8K, 16K) Schreibmodi
• Teilweises Schreiben von Seiten zulässig
• Selbstgesteuertes Schreiben Zyklus (maximal 5 ms)
• Hohe Zuverlässigkeit
– Ausdauer: 1 Million Schreibzyklen
– Datenspeicherung: 100 Jahre
• Verfügbare Kfz-Geräte
• 8-poliges JEDEC PDIP, 8-poliges JEDEC SOIC, 8-poliges Ultra Thin Mini-MAP (MLP 2x3), 5-poliges
SOT23, 8-poliges TSSOP und 8-Ball dBGA2 Pakete
• Die-Verkäufe: Waffelform, Waffelpackung und Bumped Wafers

Übersetzte Version:

[AT24002-Funktionen]
1. Niederspannungs- und Standardspannungsversionen;
2. AT24002: 256*8 (2K)
3. IIC-Protokoll;
4. Kommunikationsrauschunterdrückung:
5. Bidirektionales Übertragungsprotokoll;
6. 400-kHz-Kommunikationsrate;
7. Hardware-Schreiben Schutz;
8. Schreibzyklus (5 ms)
9. Hohe Zuverlässigkeit: 1 Million Schreibvorgänge, Daten können 100 Jahre lang gespeichert werden!

Programmierung:

  1. Kopieren Sie die i2c-halc- und h-Dateien in [Resource Data Package] nach [Programming Project]
  2. Der IO-Initialisierungscode des in main.c aufgerufenen IIC-Teils; PB6, PB7)
  3. Programmierung in der Datei i2c.c: EEPROM-Funktionen lesen und schreiben;
  4. Rufen Sie die Funktionen EEPROM Write und EEPROM Read in main.c auf, um das EEPROM-Lese- und Schreibprogramm abzuschließen.

i2c.c:

#include "i2c.h"

#define DELAY_TIME	20


/**
  * @brief SDA线输入模式配置
  * @param None
  * @retval None
  */
void SDA_Input_Mode()
{
    
    
    GPIO_InitTypeDef GPIO_InitStructure = {
    
    0};

    GPIO_InitStructure.Pin = GPIO_PIN_7;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
  * @brief SDA线输出模式配置
  * @param None
  * @retval None
  */
void SDA_Output_Mode()
{
    
    
    GPIO_InitTypeDef GPIO_InitStructure = {
    
    0};

    GPIO_InitStructure.Pin = GPIO_PIN_7;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
  * @brief SDA线输出一个位
  * @param val 输出的数据
  * @retval None
  */
void SDA_Output( uint16_t val )
{
    
    
    if ( val )
    {
    
    
        GPIOB->BSRR |= GPIO_PIN_7;
    }
    else
    {
    
    
        GPIOB->BRR |= GPIO_PIN_7;
    }
}

/**
  * @brief SCL线输出一个位
  * @param val 输出的数据
  * @retval None
  */
void SCL_Output( uint16_t val )
{
    
    
    if ( val )
    {
    
    
        GPIOB->BSRR |= GPIO_PIN_6;
    }
    else
    {
    
    
        GPIOB->BRR |= GPIO_PIN_6;
    }
}

/**
  * @brief SDA输入一位
  * @param None
  * @retval GPIO读入一位
  */
uint8_t SDA_Input(void)
{
    
    
	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){
    
    
		return 1;
	}else{
    
    
		return 0;
	}
}


/**
  * @brief I2C的短暂延时
  * @param None
  * @retval None
  */
static void delay1(unsigned int n)
{
    
    
    uint32_t i;
    for ( i = 0; i < n; ++i);
}

/**
  * @brief I2C起始信号
  * @param None
  * @retval None
  */
void I2CStart(void)
{
    
    
    SDA_Output(1);
    delay1(DELAY_TIME);
    SCL_Output(1);
    delay1(DELAY_TIME);
    SDA_Output(0);
    delay1(DELAY_TIME);
    SCL_Output(0);
    delay1(DELAY_TIME);
}

/**
  * @brief I2C结束信号
  * @param None
  * @retval None
  */
void I2CStop(void)
{
    
    
    SCL_Output(0);
    delay1(DELAY_TIME);
    SDA_Output(0);
    delay1(DELAY_TIME);
    SCL_Output(1);
    delay1(DELAY_TIME);
    SDA_Output(1);
    delay1(DELAY_TIME);

}

/**
  * @brief I2C等待确认信号
  * @param None
  * @retval None
  */
unsigned char I2CWaitAck(void)
{
    
    
    unsigned short cErrTime = 5;
    SDA_Input_Mode();
    delay1(DELAY_TIME);
    SCL_Output(1);
    delay1(DELAY_TIME);
    while(SDA_Input())
    {
    
    
        cErrTime--;
        delay1(DELAY_TIME);
        if (0 == cErrTime)
        {
    
    
            SDA_Output_Mode();
            I2CStop();
            return ERROR;
        }
    }
    SDA_Output_Mode();
    SCL_Output(0);
    delay1(DELAY_TIME);
    return SUCCESS;
}

/**
  * @brief I2C发送确认信号
  * @param None
  * @retval None
  */
void I2CSendAck(void)
{
    
    
    SDA_Output(0);
    delay1(DELAY_TIME);
    delay1(DELAY_TIME);
    SCL_Output(1);
    delay1(DELAY_TIME);
    SCL_Output(0);
    delay1(DELAY_TIME);

}

/**
  * @brief I2C发送非确认信号
  * @param None
  * @retval None
  */
void I2CSendNotAck(void)
{
    
    
    SDA_Output(1);
    delay1(DELAY_TIME);
    delay1(DELAY_TIME);
    SCL_Output(1);
    delay1(DELAY_TIME);
    SCL_Output(0);
    delay1(DELAY_TIME);

}

/**
  * @brief I2C发送一个字节
  * @param cSendByte 需要发送的字节
  * @retval None
  */
void I2CSendByte(unsigned char cSendByte)
{
    
    
    unsigned char  i = 8;
    while (i--)
    {
    
    
        SCL_Output(0);
        delay1(DELAY_TIME);
        SDA_Output(cSendByte & 0x80);
        delay1(DELAY_TIME);
        cSendByte += cSendByte;
        delay1(DELAY_TIME);
        SCL_Output(1);
        delay1(DELAY_TIME);
    }
    SCL_Output(0);
    delay1(DELAY_TIME);
}

/**
  * @brief I2C接收一个字节
  * @param None
  * @retval 接收到的字节
  */
unsigned char I2CReceiveByte(void)
{
    
    
    unsigned char i = 8;
    unsigned char cR_Byte = 0;
    SDA_Input_Mode();
    while (i--)
    {
    
    
        cR_Byte += cR_Byte;
        SCL_Output(0);
        delay1(DELAY_TIME);
        delay1(DELAY_TIME);
        SCL_Output(1);
        delay1(DELAY_TIME);
        cR_Byte |=  SDA_Input();
    }
    SCL_Output(0);
    delay1(DELAY_TIME);
    SDA_Output_Mode();
    return cR_Byte;
}

//
void I2CInit(void)
{
    
    
		GPIO_InitTypeDef GPIO_InitStructure = {
    
    0};

    GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}

i2c.h:

#ifndef __I2C_H
#define __I2C_H

#include "main.h"

void I2CStart(void);
void I2CStop(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(void);
void I2CSendNotAck(void);
void I2CSendByte(unsigned char cSendByte);
unsigned char I2CReceiveByte(void);
void I2CInit(void);

#endif

i2c.cWährend des Wettbewerbs müssen Sie die Funktionen zum Lesen und Schreiben des EEPROMs gemäß der oben angegebenen Routine und dem zuvor erwähnten IIC-Protokoll in die Datei schreiben (denken Sie daran i2c.h, es nach dem Schreiben zu deklarieren):

//写24C02
void EEPROM_Write(u8 add,u8 dat)
{
    
    
	I2CStart(); 
	I2CSendByte(0xa0); 
	I2CWaitAck(); 
	
	I2CSendByte(add);	
	I2CWaitAck(); 
	I2CSendByte(dat); 
	I2CWaitAck(); 
	I2CStop();
	HAL_Delay(5);
}
//读24C02
u8 EEPROM_Read(u8 add)
{
    
    
	u8 dat;
	
	I2CStart(); 
	I2CSendByte(0xa0);
	I2CWaitAck(); 	
	I2CSendByte(add);
	I2CWaitAck(); 
	
	I2CStart();
	I2CSendByte(0xa1); 
	I2CWaitAck();
	dat = I2CReceiveByte(); 
	I2CSendNotAck();
	I2CStop();
	
	return(dat);
}

Dann können wir main.cdie Lese- und Schreibfunktionen in der Datei aufrufen, um das EEPROM zu testen:

Beachten Sie, dass der Adressbereich des EEPROM 0 bis 0xff beträgt, also 0 bis 255. Der geschriebene Wertebereich liegt ebenfalls zwischen 0 und 255. (Da die maximale Anzahl geschriebener Ziffern 8 Ziffern beträgt, beträgt die maximale Anzahl 0xff, also 255.)

main.c lautet wie folgt:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "led.h"
#include "key.h"
#include "i2c.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

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

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//Led执行程序
__IO uint32_t ledTick =0,keyTick=0;
u8 led_ctrl=0xff;
void LED_Process(void)
{
    
    
	if(uwTick-ledTick<500)return;
	ledTick=uwTick;
	LED_Control(led_ctrl);
	led_ctrl=~led_ctrl;
}
void KEY_Process(void)
{
    
    
	if(uwTick-keyTick<10)return;
	keyTick=uwTick;
	Key_Read();
//	if(Trg&0x01)
//	{
    
    
//	LED_Control(0x01);
//	}
	if(Trg)
	{
    
    
		LED_Control(Trg);
	}
}

void LCD_Process(void)
{
    
    
	u8 display_buf[20];
	//[问题]长数据对端数据的覆盖问题
	sprintf((char*)display_buf,"%d",4000);
	LCD_DisplayStringLine(Line0,display_buf);
	sprintf((char*)display_buf,"%d",10);
	LCD_DisplayStringLine(Line0,display_buf);
	//解决方案:加空格,针对字符串
	LCD_DisplayStringLine(Line2,"hello");
	LCD_DisplayStringLine(Line2,"h     ");
	//解决方案:格式化输出,针对数据
	sprintf((char*)display_buf,"%5d",5000);//默认5位,显示右对齐
	LCD_DisplayStringLine(Line3,display_buf);
	sprintf((char*)display_buf,"%5d",10);
	LCD_DisplayStringLine(Line3,display_buf);
	
	sprintf((char*)display_buf,"%-5d",10);//左对齐
	LCD_DisplayStringLine(Line4,display_buf);
	
	sprintf((char*)display_buf,"%05d",500);//前面补0
	LCD_DisplayStringLine(Line5,display_buf);
	
	sprintf((char*)display_buf,"%5.2f",3.1415926);//显示小鼠,总长是5位,小数点算一位
	LCD_DisplayStringLine(Line6,display_buf);
	
	sprintf((char*)display_buf,"%x",15);
	LCD_DisplayStringLine(Line7,display_buf);//%x显示16进制,%o显示8进制
	
  sprintf((char*)display_buf,"%c",'a');
	LCD_DisplayStringLine(Line8,display_buf);//%s字符串,%c是字符
	
	sprintf((char*)display_buf,"%d %%",10);
	LCD_DisplayStringLine(Line9,display_buf);//输出百分号:%
}
u8 val_24c02=0;

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    
    
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
	
	LCD_Init();
	LED_Control(0x00);
	//LCD_Process();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	LCD_Process();
  I2CInit();
	EEPROM_Write(0x10,0x55);
	val_24c02=EEPROM_Read(0x10);
	u8 display_buf[20];
  while (1)
  {
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    //LED_Process();
		sprintf((char*)display_buf,"EEPROM:%d",val_24c02);
  	LCD_DisplayStringLine(Line1,display_buf);//输出百分号:%
		KEY_Process();
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    
    
  RCC_OscInitTypeDef RCC_OscInitStruct = {
    
    0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {
    
    0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
  RCC_OscInitStruct.PLL.PLLN = 20;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    
    
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    
    
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    
    
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Supongo que te gusta

Origin blog.csdn.net/Gorege__Hu/article/details/129911933
Recomendado
Clasificación