From a6cb7e50f83c3faf40ac0d856bf21dc48a9be7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=99=9F=E6=98=8A?= Date: Mon, 28 Apr 2025 11:23:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A9=B1=E5=8A=A8=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/c_cpp_properties.json | 18 + .vscode/launch.json | 24 + .vscode/settings.json | 59 ++ ADC/ad7124.c | 455 +++++++++++++++ ADC/ad7124.h | 309 ++++++++++ DAC/dac161p997.c | 154 +++++ DAC/dac161p997.h | 83 +++ DAC/dac161s997.c | 56 ++ DAC/dac161s997.h | 28 + README.md | 0 epprom/eeprom_fm24.c | 227 ++++++++ epprom/eeprom_fm24.h | 153 +++++ epprom/eeprom_lc02b.c | 172 ++++++ epprom/eeprom_lc02b.h | 60 ++ epprom/eeprom_m95.c | 330 +++++++++++ epprom/eeprom_m95.h | 168 ++++++ gp8302.c | 68 +++ gp8302.h | 22 + ntc_3950.c | 118 ++++ ntc_3950.h | 15 + pt100.c | 203 +++++++ pt100.h | 28 + rtc_rx8010.c | 560 ++++++++++++++++++ rtc_rx8010.h | 99 ++++ sht40.c | 181 ++++++ sht40.h | 9 + ssd1306_oled.c | 1011 +++++++++++++++++++++++++++++++++ ssd1306_oled.h | 80 +++ tca9555.c | 129 +++++ tca9555.h | 78 +++ tmc2240.c | 368 ++++++++++++ tmc2240.h | 372 ++++++++++++ 32 files changed, 5637 insertions(+) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 ADC/ad7124.c create mode 100644 ADC/ad7124.h create mode 100644 DAC/dac161p997.c create mode 100644 DAC/dac161p997.h create mode 100644 DAC/dac161s997.c create mode 100644 DAC/dac161s997.h create mode 100644 README.md create mode 100644 epprom/eeprom_fm24.c create mode 100644 epprom/eeprom_fm24.h create mode 100644 epprom/eeprom_lc02b.c create mode 100644 epprom/eeprom_lc02b.h create mode 100644 epprom/eeprom_m95.c create mode 100644 epprom/eeprom_m95.h create mode 100644 gp8302.c create mode 100644 gp8302.h create mode 100644 ntc_3950.c create mode 100644 ntc_3950.h create mode 100644 pt100.c create mode 100644 pt100.h create mode 100644 rtc_rx8010.c create mode 100644 rtc_rx8010.h create mode 100644 sht40.c create mode 100644 sht40.h create mode 100644 ssd1306_oled.c create mode 100644 ssd1306_oled.h create mode 100644 tca9555.c create mode 100644 tca9555.h create mode 100644 tmc2240.c create mode 100644 tmc2240.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..ff92585 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "C:/TDM-GCC-64/bin/gcc.exe", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ae363c1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": true, + "cwd": "e:/work/stm32/driver/DAC", + "program": "e:/work/stm32/driver/DAC/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c9e66f1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/ADC/ad7124.c b/ADC/ad7124.c new file mode 100644 index 0000000..d6e2a0f --- /dev/null +++ b/ADC/ad7124.c @@ -0,0 +1,455 @@ +#include "ad7124.h" +/* Error codes */ +#define INVALID_VAL -1 /* Invalid argument */ +#define COMM_ERR -2 /* Communication error on receive */ +#define AD7124_TIMEOUT -3 /* A timeout has occured */ + +// 配置ad7124寄存器的值,根据实际项目需求配置 +static ad7124_st_reg_t ad7124_regs[AD7124_REG_NO] = { + {AD7124_STATUS, 0x00, AD7124_SIZE_1, AD7124_R}, /* AD7124_Status */ + {AD7124_ADC_CONTROL, 0x0280, AD7124_SIZE_2, AD7124_RW}, /* AD7124_ADC_Control */ + {AD7124_DATA, 0x000000, AD7124_SIZE_3, AD7124_R}, /* AD7124_Data */ + {AD7124_IOCON1, 0x000000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_IOCon1 */ + {AD7124_IOCON2, 0x0000, AD7124_SIZE_2, AD7124_RW}, /* AD7124_IOCon2 */ + {AD7124_ID, 0x02, AD7124_SIZE_1, AD7124_R}, /* AD7124_ID */ + {AD7124_ERROR, 0x000000, AD7124_SIZE_3, AD7124_R}, /* AD7124_Error */ + {AD7124_ERROR_EN, 0x000040, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Error_En */ + {AD7124_MCLK_COUNT, 0x00, AD7124_SIZE_1, AD7124_R}, /* AD7124_Mclk_Count */ + {AD7124_CHANNEL_0, 0x0051, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_0 */ + {AD7124_CHANNEL_1, 0x0071, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_1 */ + {AD7124_CHANNEL_2, 0x0091, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_2 */ + {AD7124_CHANNEL_3, 0x00B1, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_3 */ + {AD7124_CHANNEL_4, 0x00D1, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_4 */ + {AD7124_CHANNEL_5, 0x00F1, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_5 */ + {AD7124_CHANNEL_6, 0x0111, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_6 */ + {AD7124_CHANNEL_7, 0x0131, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_7 */ + {AD7124_CHANNEL_8, 0x0151, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_8 */ + {AD7124_CHANNEL_9, 0x0171, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_9 */ + {AD7124_CHANNEL_10, 0x0001, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_10 */ + {AD7124_CHANNEL_11, 0x0001, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_11 */ + {AD7124_CHANNEL_12, 0x0001, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_12 */ + {AD7124_CHANNEL_13, 0x0001, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_13 */ + {AD7124_CHANNEL_14, 0x0001, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_14 */ + {AD7124_CHANNEL_15, 0x0001, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_15 */ + {AD7124_CONFIG_0, 0x01E0, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_0 */ + {AD7124_CONFIG_1, 0x0040, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_1 */ + {AD7124_CONFIG_2, 0x0860, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_2 */ + {AD7124_CONFIG_3, 0x0860, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_3 */ + {AD7124_CONFIG_4, 0x0860, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_4 */ + {AD7124_CONFIG_5, 0x0860, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_5 */ + {AD7124_CONFIG_6, 0x0860, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_6 */ + {AD7124_CONFIG_7, 0x0860, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Config_7 */ + {AD7124_FILTER_0, 0x060020, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_0 */ + {AD7124_FILTER_1, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_1 */ + {AD7124_FILTER_2, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_2 */ + {AD7124_FILTER_3, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_3 */ + {AD7124_FILTER_4, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_4 */ + {AD7124_FILTER_5, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_5 */ + {AD7124_FILTER_6, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_6 */ + {AD7124_FILTER_7, 0x060180, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Filter_7 */ + {AD7124_OFFSET_0, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_0 */ + {AD7124_OFFSET_1, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_1 */ + {AD7124_OFFSET_2, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_2 */ + {AD7124_OFFSET_3, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_3 */ + {AD7124_OFFSET_4, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_4 */ + {AD7124_OFFSET_5, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_5 */ + {AD7124_OFFSET_6, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_6 */ + {AD7124_OFFSET_7, 0x800000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Offset_7 */ + {AD7124_GAIN_0, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_0 */ + {AD7124_GAIN_1, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_1 */ + {AD7124_GAIN_2, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_2 */ + {AD7124_GAIN_3, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_3 */ + {AD7124_GAIN_4, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_4 */ + {AD7124_GAIN_5, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_5 */ + {AD7124_GAIN_6, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_6 */ + {AD7124_GAIN_7, 0x500000, AD7124_SIZE_3, AD7124_RW}, /* AD7124_Gain_7 */ +}; + +static ad7124_st_reg_t ad7124_channel_regs[AD7124_CHANNEL_EN_MAX] = { + {AD7124_CHANNEL_0, 0x8051, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_0 */ + {AD7124_CHANNEL_1, 0x8071, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_1 */ + {AD7124_CHANNEL_2, 0x8091, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_2 */ + {AD7124_CHANNEL_3, 0x80B1, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_3 */ + {AD7124_CHANNEL_4, 0x80D1, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_4 */ + {AD7124_CHANNEL_5, 0x80F1, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_5 */ + {AD7124_CHANNEL_6, 0x8111, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_6 */ + {AD7124_CHANNEL_7, 0x8131, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_7 */ + {AD7124_CHANNEL_8, 0x8151, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_8 */ + {AD7124_CHANNEL_9, 0x8171, AD7124_SIZE_2, AD7124_RW}, /* AD7124_Channel_9 */ +}; + +ad7124_analog_t ad7124_analog[AD7124_CHANNEL_EN_MAX] = {NULL}; // AD通道采样结构体数组,用于存放AD通道采样数据 +/** + * @brief 无校验读取AD7124寄存器 + * + * 读取AD7124的寄存器值,不进行校验。 + * + * @param p_reg 指向要读取的寄存器结构的指针 + * + * @return 读取成功返回0,失败返回负值 + */ +int32_t ad7124_no_check_read_register(ad7124_st_reg_t *p_reg) +{ + int32_t ret = 0; + uint8_t buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t i = 0; + uint8_t add_status_length = 0; + + /* Build the Command word */ + buffer[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_RD | + AD7124_COMM_REG_RA(p_reg->addr); + + /* + * If this is an AD7124_DATA register read, and the DATA_STATUS bit is set + * in ADC_CONTROL, need to read 4, not 3 bytes for DATA with STATUS + */ + if ((p_reg->addr == AD7124_DATA) && + (ad7124_regs[AD7124_ADC_CONTROL].value & AD7124_ADC_CTRL_REG_DATA_STATUS)) + { + add_status_length = 1; // 此处是根据寄存器配置而决定,根据AD7124_ADC_CONTROL第10位决定 + } + + /* Read data from the device */ + ret = ad7124_read_write_spi(buffer, p_reg->size + 1 + add_status_length); + if (ret < 0) + return ret; + + /* + * if reading Data with 4 bytes, need to copy the status byte to the STATUS + * register struct value member + */ + if (add_status_length) + { + ad7124_regs[AD7124_STATUS].value = buffer[p_reg->size + 1]; + } + + /* Build the result */ + p_reg->value = 0; + for (i = 1; i < p_reg->size + 1; i++) + { + p_reg->value <<= 8; + p_reg->value += buffer[i]; + } + + return ret; +} + +/** + * @brief 向AD7124寄存器写入数据,不进行校验 + * + * 将数据写入指定的AD7124寄存器,不进行数据校验。 + * + * @param reg 指向要写入数据的寄存器地址和值的指针 + * @return 写入结果,成功返回0,失败返回负值 + */ +int32_t ad7124_no_check_write_register(ad7124_st_reg_t *reg) +{ + int32_t ret = 0; + int32_t reg_value = 0; + uint8_t wr_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t i = 0; + + /* Build the Command word */ + wr_buf[0] = AD7124_COMM_REG_WEN | AD7124_COMM_REG_WR | + AD7124_COMM_REG_RA(reg->addr); + + /* Fill the write buffer */ + reg_value = reg->value; + for (i = 0; i < reg->size; i++) + { + wr_buf[reg->size - i] = reg_value & 0xFF; + reg_value >>= 8; + } + + /* Write data to the device */ + ret = ad7124_read_write_spi(wr_buf, reg->size + 1); + + return ret; +} + +/** + * @brief 从AD7124寄存器读取数据 + * + * 该函数用于从AD7124的指定寄存器中读取数据。 + * + * @param p_reg 指向一个ad7124_st_reg_t类型的指针,其中包含了要读取的寄存器的地址。 + * + * @return 返回读取到的数据,如果读取失败则返回负数。 + * + * 在读取寄存器之前,会先检查ERROR寄存器的SPI_IGNORE_ERR位 + */ +int32_t ad7124_read_register(ad7124_st_reg_t *p_reg) +{ + int32_t ret; + + if (p_reg->addr != AD7124_ERROR && (ad7124_regs[AD7124_ERROR_EN].value & AD7124_ERREN_REG_SPI_IGNORE_ERR_EN)) + { + ret = ad7124_wait_for_spi_ready(AD7124_RDY); // 读寄存器之前检查ERROR寄存器的SPI_IGNORE_ERR位 + + if (ret < 0) + return ret; + } + ret = ad7124_no_check_read_register(p_reg); + + return ret; +} + +/** + * @brief 向AD7124寄存器写入数据 + * + * 将数据写入AD7124的指定寄存器。如果错误寄存器中启用了忽略SPI错误的功能,则在写入前会等待SPI接口准备就绪。 + * + * @param p_reg 指向要写入数据的寄存器结构体的指针 + * + * @return 返回写入操作的结果。如果操作成功,则返回0;否则返回负数表示错误。 + */ +int32_t ad7124_write_register(ad7124_st_reg_t *p_reg) +{ + int32_t ret; + + if ((ad7124_regs[AD7124_ERROR_EN].value & AD7124_ERREN_REG_SPI_IGNORE_ERR_EN)) + { + ret = ad7124_wait_for_spi_ready(AD7124_RDY); + if (ret < 0) + return ret; + } + ret = ad7124_no_check_write_register(p_reg); + + return ret; +} + +/** + * @brief 重置AD7124设备 + * + * 该函数通过SPI接口向AD7124发送重置命令,并等待设备完成上电过程。 + * + * @return 返回函数执行结果,成功返回0,失败返回错误码。 + */ +int32_t ad7124_reset(void) +{ + int32_t ret = 0; + uint8_t wr_buf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 发送64个1,复位ad7124芯片 + + ret = ad7124_read_write_spi(wr_buf, 8); + + /* Read POR bit to clear */ + ret = ad7124_wait_to_power_on(AD7124_RDY); + + return ret; +} + +/** + * @brief 等待SPI接口准备就绪 + * + * 等待AD7124的SPI接口准备就绪,直到超时。 + * + * @param timeout 超时时间(单位:毫秒) + * @return 成功返回0,超时返回AD7124_TIMEOUT + */ +int32_t ad7124_wait_for_spi_ready(uint32_t timeout) +{ + int32_t ret; + int8_t ready = 0; + + while (!ready && --timeout) + { + /* Read the value of the Error Register */ + ret = ad7124_read_register(&ad7124_regs[AD7124_ERROR]); + if (ret < 0) + return ret; + + /* Check the SPI IGNORE Error bit in the Error Register */ + ready = (ad7124_regs[AD7124_ERROR].value & + AD7124_ERR_REG_SPI_IGNORE_ERR) == 0; + } + + return timeout ? 0 : AD7124_TIMEOUT; +} + +/** + * @brief 等待AD7124电源开启 + * + * 此函数将等待AD7124设备电源开启,直到超时或检测到电源开启标志。 + * + * @param timeout 超时时间 + * + * @return 返回0表示成功,返回AD7124_TIMEOUT表示超时 + */ +int32_t ad7124_wait_to_power_on(uint32_t timeout) +{ + int32_t ret; + int8_t powered_on = 0; + + while (!powered_on && timeout--) + { + ret = ad7124_read_register(&ad7124_regs[AD7124_STATUS]); + if (ret < 0) + return ret; + + /* Check the POR_FLAG bit in the Status Register */ + powered_on = (ad7124_regs[AD7124_STATUS].value & + AD7124_STATUS_REG_POR_FLAG) == 0; + } + + return (timeout || powered_on) ? 0 : AD7124_TIMEOUT; +} + +/** + * @brief 等待AD7124转换准备就绪 + * + * 该函数等待AD7124完成转换并准备就绪。如果等待超时,则返回错误码。 + * + * @param timeout 超时时间 + * @return 成功时返回0,超时时返回AD7124_TIMEOUT + */ +int32_t ad7124_wait_for_conv_ready(uint32_t timeout) +{ + int32_t ret; + int8_t ready = 0; + + while (!ready && --timeout) + { + /* Read the value of the Status Register */ + ret = ad7124_read_register(&ad7124_regs[AD7124_STATUS]); + if (ret < 0) + return ret; + + /* Check the RDY bit in the Status Register */ + ready = (ad7124_regs[AD7124_STATUS].value & + AD7124_STATUS_REG_RDY) == 0; + } + + return timeout ? 0 : AD7124_TIMEOUT; +} + +/** + * @brief 从AD7124读取数据 + * + * 从AD7124的数据寄存器中读取数据值。 + * + * @return 读取到的数据值 + */ +int32_t ad7124_read_data(void) +{ + int32_t read_data; + /* Read the value of the Status Register */ + ad7124_read_register(&ad7124_regs[AD7124_DATA]); + /* Get the read result */ + read_data = ad7124_regs[AD7124_DATA].value; + + return read_data; +} +/** + * @brief 从AD7124获取模拟信号数据 + * + * 该函数通过SPI接口从AD7124芯片获取模拟信号数据,并将数据保存到ad7124_analog中。 + * + * @details + * - 遍历所有模拟通道,对每个通道执行以下操作: + * - 等待AD7124完成转换。 + * - 读取转换后的数据。 + * - 计算电压值(单极性计算公式)。 + * - 计算电流值(基于电压值和电阻值),有的通道没有电流值可以忽略。 + * + * @note + * - 转换完成后,通过公式计算电压和电流值,并将结果保存到全局数组ad7124_analog中。 + * - 电压计算公式:Code = (0xFFFFFF × AIN × Gain)/VREF,其中VREF为参考电压,GAIN为增益,AD_CODE为AD代码。 + * - 电流计算公式:电流 = 电压 / 电阻 * 1000,其中电阻值为AD7124_RES,单位转换为mA。 + */ +// void ad7124_get_analog(void) +// { +// int32_t read_data; +// uint8_t i; +// uint8_t channel; +// // for (i = STOP_NC_ADC; i < AD7124_CHANNEL_EN_MAX; i++) +// // { +// // ad7124_regs[AD7124_CHANNEL_0].value = ad7124_channel_regs[i].value; +// // ad7124_write_register(&ad7124_regs[AD7124_CHANNEL_0]); + +// // ad7124_read_register(&ad7124_regs[AD7124_STATUS]); +// while (ad7124_wait_for_conv_ready(AD7124_RDY)) +// ; // 等待转换完成 +// channel = ad7124_regs[AD7124_STATUS].value; +// ad7124_analog[channel].channel = channel; +// read_data = ad7124_read_data(); +// ad7124_analog[channel].data = read_data; +// ad7124_analog[channel].voltage = (float)(read_data * VREF / GAIN / AD_CODE); // AD7124单极性计算公式:Code = (0xFFFFFF × AIN × Gain)/VREF +// ad7124_analog[channel].current = (float)(ad7124_analog[channel].voltage / AD7124_RES * 1000); // 乘1000是为了将单位转换为mA + +// // ad7124_regs[AD7124_CHANNEL_0].value = 0; +// // ad7124_write_register(&ad7124_regs[AD7124_CHANNEL_0]); +// // } +// } +void ad7124_get_analog(uint8_t channel_nr) +{ + int32_t read_data; + // for (i = STOP_NC_ADC; i < AD7124_CHANNEL_EN_MAX; i++) + // { + ad7124_regs[AD7124_CHANNEL_0].value = ad7124_channel_regs[channel_nr].value; + ad7124_write_register(&ad7124_regs[AD7124_CHANNEL_0]); + + while (ad7124_wait_for_conv_ready(AD7124_RDY)) + ; // 等待转换完成 + ad7124_analog[channel_nr].channel = channel_nr; + read_data = ad7124_read_data(); + ad7124_analog[channel_nr].data = read_data; + ad7124_analog[channel_nr].voltage = (float)(read_data * VREF / GAIN / AD_CODE); // AD7124单极性计算公式:Code = (0xFFFFFF × AIN × Gain)/VREF + ad7124_analog[channel_nr].current = (float)(ad7124_analog[channel_nr].voltage / AD7124_RES * 1000); // 乘1000是为了将单位转换为mA + + ad7124_regs[AD7124_CHANNEL_0].value = 0; + ad7124_write_register(&ad7124_regs[AD7124_CHANNEL_0]); + // } +} +/** + * @brief 配置AD7124设备 + * + * 该函数负责初始化AD7124设备,初始化寄存器。 + * + * @return 初始化结果,成功返回0,失败返回负数 + */ +int32_t ad7124_setup(void) +{ + int32_t ret; + uint8_t reg_nr; + // board_spi_init(AD7124); // 初始化SPI,因为DAC161的SPI要和AD7124共用,并且时序不一样,所以要先初始化SPI接口。 + /* Reset the device interface.*/ + ret = ad7124_reset(); + if (ret < 0) + return ret; + HAL_Delay(10); + ad7124_read_register(&ad7124_regs[AD7124_ID]); + /* Initialize registers AD7124_ADC_Control through AD7124_Filter_7. */ + for (reg_nr = AD7124_STATUS; reg_nr < AD7124_OFFSET_0; reg_nr++) // 对ad7124的可写寄存器进行配置,不包括只读寄存器 + { + // ret = ad7124_read_register(&ad7124_regs[reg_nr]); + if (ad7124_regs[reg_nr].rw == AD7124_RW) + { + ret = ad7124_write_register(&ad7124_regs[reg_nr]); + ret = ad7124_read_register(&ad7124_regs[reg_nr]); + if (ret < 0) + break; + } + } + HAL_GPIO_WritePin(AD7124_SYNC_GPIO_Port, AD7124_SYNC_Pin, GPIO_PIN_SET); // AD7124同步信号使能 + return ret; +} + +/** + * @brief 通过SPI接口对AD7124进行读写操作 + * + * 通过SPI接口对AD7124进行读写操作,。 + * + * @param buff 指向待传输数据的指针 + * @param length 待传输数据的长度 + * + * @return 传输结果,成功返回0,失败返回负数 + */ +int32_t ad7124_read_write_spi(uint8_t *buff, uint8_t length) +{ + int32_t ret; + board_spi_init(AD7124); // 初始化SPI,因为DAC161的SPI要和AD7124共用,并且时序不一样,所以要先初始化SPI接口。 + board_spi_cs_on(AD7124); + ret = spi_transmit_receive(&hspi1, buff, length); + board_spi_cs_off(AD7124); + return ret; +} diff --git a/ADC/ad7124.h b/ADC/ad7124.h new file mode 100644 index 0000000..1fb12be --- /dev/null +++ b/ADC/ad7124.h @@ -0,0 +1,309 @@ +#ifndef __AD7124_H +#define __AD7124_H + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "main.h" +#include "user_spi.h" +/******************************************************************************/ +/******************* Register map and register definitions ********************/ +/******************************************************************************/ + +/* Communication Register bits */ +#define AD7124_COMM_REG_WEN (0 << 7) +#define AD7124_COMM_REG_WR (0 << 6) +#define AD7124_COMM_REG_RD (1 << 6) +#define AD7124_COMM_REG_RA(x) ((x) & 0x3F) + +/* Status Register bits */ +#define AD7124_STATUS_REG_RDY (1 << 7) +#define AD7124_STATUS_REG_ERROR_FLAG (1 << 6) +#define AD7124_STATUS_REG_POR_FLAG (1 << 4) +#define AD7124_STATUS_REG_CH_ACTIVE(x) ((x) & 0xF) + +/* ADC_Control Register bits */ +#define AD7124_ADC_CTRL_REG_DOUT_RDY_DEL (1 << 12) +#define AD7124_ADC_CTRL_REG_CONT_READ (1 << 11) +#define AD7124_ADC_CTRL_REG_DATA_STATUS (1 << 10) +#define AD7124_ADC_CTRL_REG_CS_EN (1 << 9) +#define AD7124_ADC_CTRL_REG_REF_EN (1 << 8) +#define AD7124_ADC_CTRL_REG_POWER_MODE(x) (((x) & 0x3) << 6) +#define AD7124_ADC_CTRL_REG_MODE(x) (((x) & 0xF) << 2) +#define AD7124_ADC_CTRL_REG_CLK_SEL(x) (((x) & 0x3) << 0) + +/* IO_Control_1 Register bits */ +#define AD7124_IO_CTRL1_REG_GPIO_DAT2 (1 << 23) +#define AD7124_IO_CTRL1_REG_GPIO_DAT1 (1 << 22) +#define AD7124_IO_CTRL1_REG_GPIO_CTRL2 (1 << 19) +#define AD7124_IO_CTRL1_REG_GPIO_CTRL1 (1 << 18) +#define AD7124_IO_CTRL1_REG_PDSW (1 << 15) +#define AD7124_IO_CTRL1_REG_IOUT1(x) (((x) & 0x7) << 11) +#define AD7124_IO_CTRL1_REG_IOUT0(x) (((x) & 0x7) << 8) +#define AD7124_IO_CTRL1_REG_IOUT_CH1(x) (((x) & 0xF) << 4) +#define AD7124_IO_CTRL1_REG_IOUT_CH0(x) (((x) & 0xF) << 0) + +/* IO_Control_1 AD7124-8 specific bits */ +#define AD7124_8_IO_CTRL1_REG_GPIO_DAT4 (1 << 23) +#define AD7124_8_IO_CTRL1_REG_GPIO_DAT3 (1 << 22) +#define AD7124_8_IO_CTRL1_REG_GPIO_DAT2 (1 << 21) +#define AD7124_8_IO_CTRL1_REG_GPIO_DAT1 (1 << 20) +#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL4 (1 << 19) +#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL3 (1 << 18) +#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL2 (1 << 17) +#define AD7124_8_IO_CTRL1_REG_GPIO_CTRL1 (1 << 16) + +/* IO_Control_2 Register bits */ +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS7 (1 << 15) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS6 (1 << 14) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS5 (1 << 11) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS4 (1 << 10) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS3 (1 << 5) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS2 (1 << 4) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS1 (1 << 1) +#define AD7124_IO_CTRL2_REG_GPIO_VBIAS0 (1 << 0) + +/* IO_Control_2 AD7124-8 specific bits */ +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS15 (1 << 15) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS14 (1 << 14) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS13 (1 << 13) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS12 (1 << 12) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS11 (1 << 11) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS10 (1 << 10) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS9 (1 << 9) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS8 (1 << 8) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS7 (1 << 7) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS6 (1 << 6) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS5 (1 << 5) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS4 (1 << 4) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS3 (1 << 3) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS2 (1 << 2) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS1 (1 << 1) +#define AD7124_8_IO_CTRL2_REG_GPIO_VBIAS0 (1 << 0) + +/* ID Register bits */ +#define AD7124_ID_REG_DEVICE_ID(x) (((x) & 0xF) << 4) +#define AD7124_ID_REG_SILICON_REV(x) (((x) & 0xF) << 0) + +/* Error Register bits */ +#define AD7124_ERR_REG_LDO_CAP_ERR (1 << 19) +#define AD7124_ERR_REG_ADC_CAL_ERR (1 << 18) +#define AD7124_ERR_REG_ADC_CONV_ERR (1 << 17) +#define AD7124_ERR_REG_ADC_SAT_ERR (1 << 16) +#define AD7124_ERR_REG_AINP_OV_ERR (1 << 15) +#define AD7124_ERR_REG_AINP_UV_ERR (1 << 14) +#define AD7124_ERR_REG_AINM_OV_ERR (1 << 13) +#define AD7124_ERR_REG_AINM_UV_ERR (1 << 12) +#define AD7124_ERR_REG_REF_DET_ERR (1 << 11) +#define AD7124_ERR_REG_DLDO_PSM_ERR (1 << 9) +#define AD7124_ERR_REG_ALDO_PSM_ERR (1 << 7) +#define AD7124_ERR_REG_SPI_IGNORE_ERR (1 << 6) +#define AD7124_ERR_REG_SPI_SLCK_CNT_ERR (1 << 5) +#define AD7124_ERR_REG_SPI_READ_ERR (1 << 4) +#define AD7124_ERR_REG_SPI_WRITE_ERR (1 << 3) +#define AD7124_ERR_REG_SPI_CRC_ERR (1 << 2) +#define AD7124_ERR_REG_MM_CRC_ERR (1 << 1) +#define AD7124_ERR_REG_ROM_CRC_ERR (1 << 0) + +/* Error_En Register bits */ +#define AD7124_ERREN_REG_MCLK_CNT_EN (1 << 22) +#define AD7124_ERREN_REG_LDO_CAP_CHK_TEST_EN (1 << 21) +#define AD7124_ERREN_REG_LDO_CAP_CHK(x) (((x) & 0x3) << 19) +#define AD7124_ERREN_REG_ADC_CAL_ERR_EN (1 << 18) +#define AD7124_ERREN_REG_ADC_CONV_ERR_EN (1 << 17) +#define AD7124_ERREN_REG_ADC_SAT_ERR_EN (1 << 16) +#define AD7124_ERREN_REG_AINP_OV_ERR_EN (1 << 15) +#define AD7124_ERREN_REG_AINP_UV_ERR_EN (1 << 14) +#define AD7124_ERREN_REG_AINM_OV_ERR_EN (1 << 13) +#define AD7124_ERREN_REG_AINM_UV_ERR_EN (1 << 12) +#define AD7124_ERREN_REG_REF_DET_ERR_EN (1 << 11) +#define AD7124_ERREN_REG_DLDO_PSM_TRIP_TEST_EN (1 << 10) +#define AD7124_ERREN_REG_DLDO_PSM_ERR_ERR (1 << 9) +#define AD7124_ERREN_REG_ALDO_PSM_TRIP_TEST_EN (1 << 8) +#define AD7124_ERREN_REG_ALDO_PSM_ERR_EN (1 << 7) +#define AD7124_ERREN_REG_SPI_IGNORE_ERR_EN (1 << 6) +#define AD7124_ERREN_REG_SPI_SCLK_CNT_ERR_EN (1 << 5) +#define AD7124_ERREN_REG_SPI_READ_ERR_EN (1 << 4) +#define AD7124_ERREN_REG_SPI_WRITE_ERR_EN (1 << 3) +#define AD7124_ERREN_REG_SPI_CRC_ERR_EN (1 << 2) +#define AD7124_ERREN_REG_MM_CRC_ERR_EN (1 << 1) +#define AD7124_ERREN_REG_ROM_CRC_ERR_EN (1 << 0) + +/* Channel Registers 0-15 bits */ +#define AD7124_CH_MAP_REG_CH_ENABLE (1 << 15) +#define AD7124_CH_MAP_REG_SETUP(x) (((x) & 0x7) << 12) +#define AD7124_CH_MAP_REG_AINP(x) (((x) & 0x1F) << 5) +#define AD7124_CH_MAP_REG_AINM(x) (((x) & 0x1F) << 0) + +/* Configuration Registers 0-7 bits */ +#define AD7124_CFG_REG_BIPOLAR (1 << 11) +#define AD7124_CFG_REG_BURNOUT(x) (((x) & 0x3) << 9) +#define AD7124_CFG_REG_REF_BUFP (1 << 8) +#define AD7124_CFG_REG_REF_BUFM (1 << 7) +#define AD7124_CFG_REG_AIN_BUFP (1 << 6) +#define AD7124_CFG_REG_AINN_BUFM (1 << 5) +#define AD7124_CFG_REG_REF_SEL(x) ((x) & 0x3) << 3 +#define AD7124_CFG_REG_PGA(x) (((x) & 0x7) << 0) + +/* Filter Register 0-7 bits */ +#define AD7124_FILT_REG_FILTER(x) (((x) & 0x7) << 21) +#define AD7124_FILT_REG_REJ60 (1 << 20) +#define AD7124_FILT_REG_POST_FILTER(x) (((x) & 0x7) << 17) +#define AD7124_FILT_REG_SINGLE_CYCLE (1 << 16) +#define AD7124_FILT_REG_FS(x) (((x) & 0x7FF) << 0) + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/*! AD7124 registers list*/ +typedef enum +{ + AD7124_STATUS = 0X00, // 只读的状态寄存器,读取芯片状态 + AD7124_ADC_CONTROL, // 控制寄存器,设置采样速率等 + AD7124_DATA, // 数据寄存器,读取采样数据 + AD7124_IOCON1, // 设置激励电流等参数,用作RTD测试时可用 + AD7124_IOCON2, // 使能偏置电压 + AD7124_ID, // ID寄存器,部分芯片的ID和手册上有所区别,可用此寄存器测试SPI通讯 + AD7124_ERROR, // 错误寄存器,读取错误信息,读写寄存器之前先读下该寄存器,检测芯片状态是否支持读写 + AD7124_ERROR_EN, // 通过使能该寄存器的相应位来使能或者禁用诊断功能 + AD7124_MCLK_COUNT, // 监控主时钟频率 + AD7124_CHANNEL_0, // 设置AD采样通道和所需要的配置,其中的Setup位决定了采用哪种Config、Filter、Offset、Gain寄存器的配置;共有八种配置 + AD7124_CHANNEL_1, // 通道寄存器的顺序并不是从AI0引脚读到最后一个引脚,而是通过自己的设置来决定顺序 + AD7124_CHANNEL_2, + AD7124_CHANNEL_3, + AD7124_CHANNEL_4, + AD7124_CHANNEL_5, + AD7124_CHANNEL_6, + AD7124_CHANNEL_7, + AD7124_CHANNEL_8, + AD7124_CHANNEL_9, + AD7124_CHANNEL_10, + AD7124_CHANNEL_11, + AD7124_CHANNEL_12, + AD7124_CHANNEL_13, + AD7124_CHANNEL_14, + AD7124_CHANNEL_15, + AD7124_CONFIG_0, // 配置极性,增益,基准选择等参数 + AD7124_CONFIG_1, + AD7124_CONFIG_2, + AD7124_CONFIG_3, + AD7124_CONFIG_4, + AD7124_CONFIG_5, + AD7124_CONFIG_6, + AD7124_CONFIG_7, + AD7124_FILTER_0, + AD7124_FILTER_1, // 配置滤波器 + AD7124_FILTER_2, + AD7124_FILTER_3, + AD7124_FILTER_4, + AD7124_FILTER_5, + AD7124_FILTER_6, + AD7124_FILTER_7, + AD7124_OFFSET_0, + AD7124_OFFSET_1, + AD7124_OFFSET_2, + AD7124_OFFSET_3, + AD7124_OFFSET_4, + AD7124_OFFSET_5, + AD7124_OFFSET_6, + AD7124_OFFSET_7, + AD7124_GAIN_0, + AD7124_GAIN_1, + AD7124_GAIN_2, + AD7124_GAIN_3, + AD7124_GAIN_4, + AD7124_GAIN_5, + AD7124_GAIN_6, + AD7124_GAIN_7, + AD7124_REG_NO +} ad7124_registers_addr_e; // 寄存器地址 +typedef enum +{ + + AD7124_SIZE_1 = 1, + AD7124_SIZE_2 = 2, + AD7124_SIZE_3 = 3, +} ad7124_registers_size_e; // 寄存器字节大小 +typedef enum +{ + AD7124_RW = 1, + AD7124_R = 2, + AD7124_W = 3, +} ad7124_registers_rw_e; // 寄存器读写操作 + +typedef struct +{ + int addr; // 寄存器地址,ad7124_registers_e + int value; // 寄存器值 + int size; // 寄存器字节大小 + int rw; // 寄存器可执行的操作,ad7124_registers_rw_e +} ad7124_st_reg_t; // AD7124寄存器结构体 + +typedef enum +{ + STOP_NC_ADC = 0, // AD7124_CHANNEL_EN_2 + STOP_NO_ADC, // AD7124_CHANNEL_EN_3 + AI_IN1_ADC, // AD7124_CHANNEL_EN_4 + AI_IN2_ADC, // AD7124_CHANNEL_EN_5 + P1_DI2_ADC, // AD7124_CHANNEL_EN_6 + P1_DI1_ADC, // AD7124_CHANNEL_EN_7 + P1_AI_ADC, // AD7124_CHANNEL_EN_8 + P2_DI2_ADC, // AD7124_CHANNEL_EN_9 + P2_DI1_ADC, // AD7124_CHANNEL_EN_10 + P2_AI_ADC, // AD7124_CHANNEL_EN_11 + AD7124_CHANNEL_EN_MAX, +} ad7124_channel_e; // 该项目所使用的通道 + +typedef struct +{ + uint8_t channel; + int32_t data; // 采样数据 + float voltage; // 电压值 + float current; // 电流值 +} ad7124_analog_t; // 采样数据结构体 + +#define AD7124_CRC8_POLYNOMIAL_REPRESENTATION 0x07 /* x8 + x2 + x + 1 */ +#define AD7124_DISABLE_CRC 0 // 默认关闭校验 +#define AD7124_USE_CRC 1 + +#define AD7124_RDY 10000 // 等待时间 + +#define AD7124_RES 100 // 采样基准电阻 +#define VREF 2.5f // 基准电压 +#define GAIN 1 // 增益,该值和配置寄存器有关 +#define AD_CODE 0XFFFFFF // 24位ADC + +/*! Reads the value of the specified register. */ +int32_t ad7124_read_register(ad7124_st_reg_t *p_reg); // 读寄存器 + +/*! Writes the value of the specified register. */ +int32_t ad7124_write_register(ad7124_st_reg_t *reg); // 写寄存器 + +/*! Reads the value of the specified register without a device state check. */ +int32_t ad7124_no_check_read_register(ad7124_st_reg_t *p_reg); + +/*! Writes the value of the specified register without a device state check. */ +int32_t ad7124_no_check_write_register(ad7124_st_reg_t *reg); + +/*! Resets the device. */ +int32_t ad7124_reset(void); // 复位ad7124芯片 + +/*! Waits until the device can accept read and write user actions. */ +int32_t ad7124_wait_for_spi_ready(uint32_t timeout); // 读取ad7124芯片状态,直到可以执行读写操作 + +/*! Waits until the device finishes the power-on reset operation. */ +int32_t ad7124_wait_to_power_on(uint32_t timeout); + +/*! Waits until a new conversion result is available. */ +int32_t ad7124_wait_for_conv_ready(uint32_t timeout); // 等待转换完成 + +/*! Reads the conversion result from the device. */ +int32_t ad7124_read_data(void); +void ad7124_get_analog(uint8_t channel_nr); +/*! Initializes the AD7124. */ +int32_t ad7124_setup(void); + +int32_t ad7124_read_write_spi(uint8_t *buff, uint8_t length); + +#endif /* __AD7124_H__ */ diff --git a/DAC/dac161p997.c b/DAC/dac161p997.c new file mode 100644 index 0000000..e7e8e79 --- /dev/null +++ b/DAC/dac161p997.c @@ -0,0 +1,154 @@ +#include "dac161p997.h" +#include "delay.h" +static dac161p997_t _handle; + +static void _delay_us(uint32_t us) +{ + delay_hardware_us(_handle.timer_us, us); +} + +static void dac161p997_output_0() +{ + _handle.io->set(*_handle.io); + _delay_us(DUTY_CYCLE_25); + _handle.io->reset(*_handle.io); + _delay_us(DUTY_CYCLE_75); +} + +static void dac161p997_output_1() +{ + _handle.io->set(*_handle.io); + _delay_us(DUTY_CYCLE_75); + _handle.io->reset(*_handle.io); + _delay_us(DUTY_CYCLE_25); +} + +static void dac161p997_output_d() +{ + _handle.io->set(*_handle.io); + _delay_us(DUTY_CYCLE_50); + _handle.io->reset(*_handle.io); + _delay_us(DUTY_CYCLE_50); +} + +/** + * @brief 输出DAC161符号 + * + * 根据传入的符号类型,调用相应的DAC161输出函数。 + * + * @param sym 符号类型,可以是ZERO_SYM、ONE_SYM或其他值。 + */ +static void dac161p997_output_symbol(uint8_t sym) +{ + switch (sym) + { + case ZERO_SYM: + dac161p997_output_0(); + break; + case ONE_SYM: + dac161p997_output_1(); + break; + default: + dac161p997_output_d(); + break; + } +} + +/** + * @brief 向DAC161写入寄存器 + * + * 向DAC161写入一个16位的数据,并附加一个8位的标签。 + * 芯片会回复一个应答信号,此处忽略应答信号。可以通过设置寄存器关闭应答信号。 + * @param data 要写入的数据,16位。 + * @param tag 附加的标签,8位。tag = 0时,写DAC寄存器;tag = 1时,写配置寄存器。 + */ +void dac161p997_swif_write_reg(uint16_t data, uint8_t tag) +{ + uint8_t plow, phigh; + uint16_t i, tmp; + + /* compute parity low */ + tmp = data & 0x00ff; // get least significant byte + for (plow = 0; tmp != 0; tmp >>= 1) + { + if (tmp & 0x1) // test if lsb is 1 + plow++; // count number of bits equal to 1 + } + + if (plow & 0x1) // check if number of 1s is odd + plow = 0; // set even parity + else + plow = 1; // else set odd parity + + /* compute parity high */ + tmp = (data & 0xff00) >> 8; // get most significant byte + for (phigh = 0; tmp != 0; tmp >>= 1) + { + if (tmp & 0x1) // test if lsb is 1 + phigh++; // count number of bits equal to 1 + } + + if (phigh & 0x1) // check if number of 1s is odd + phigh = 0; // set even parity + else + phigh = 1; // set odd parity + + phigh = phigh ^ tag; // parity high is for high slice = tag + 1 byte + + dac161p997_output_symbol(IDLE_SYM); // Frame start: send an idle symbol first + dac161p997_output_symbol(tag); + + tmp = data; + for (i = 0; i < 16; i++) // send 16 data bits msb to lsb + { + if (tmp & 0x8000) + { + dac161p997_output_symbol(ONE_SYM); // send 1 + } + else + { + dac161p997_output_symbol(ZERO_SYM); // send 0 + } + + tmp = tmp << 1; // move next data bit to msb + } + + dac161p997_output_symbol(phigh); // send parity high bit + dac161p997_output_symbol(plow); // send parity low bit + + dac161p997_output_symbol(IDLE_SYM); // send idle +} + +/** + * @brief 设置DAC161输出电流 + * + * 通过向DAC161写入指定的电流值来设置其输出电流,输出电流持续时间100ms。 + * + * @param current 要设置的电流值 + */ +void dac161p997_output_current(float32 current) +{ + uint16_t adc = (uint16_t)(current * DAC161P997_CURRENT_SLOPE); + dac161p997_swif_write_reg(adc, DACCODE_WRITE); +} + +/** + * @brief 初始化DAC161设备 + * + * 此函数用于初始化DAC161设备,进行必要的配置,以便设备正常工作。 + * 写寄存器之前要先解锁,具体配置寄存器根据需要配置的寄存器而定 + * 具体步骤包括: + * 1. 解锁DAC161的配置寄存器 + * 2. 配置DAC161的错误下限寄存器 + * 3. 锁定DAC161的配置寄存器 + */ +void dac161p997_init(TIM_TypeDef *timer_us) +{ + DBG_ASSERT(timer_us != NULL __DBG_LINE); + _handle.timer_us = timer_us; + ENABLE_TIM_COUNT(_handle.timer_us); + _handle.io = gpio_create(DAC161P997_IO_PORT, DAC161P997_IO_PIN); + dac161p997_swif_write_reg(DAC161P997_LCK_REG + DAC161P997_LCK_REG_UNLOCK, CONFIG_WRITE); + dac161p997_swif_write_reg(DAC161P997_CONFIG2_REG, CONFIG_WRITE); + dac161p997_swif_write_reg(DAC161P997_LCK_REG + DAC161P997_LCK_REG_LOCK, CONFIG_WRITE); +} diff --git a/DAC/dac161p997.h b/DAC/dac161p997.h new file mode 100644 index 0000000..9c480c7 --- /dev/null +++ b/DAC/dac161p997.h @@ -0,0 +1,83 @@ +#ifndef __DAC161P997_H__ +#define __DAC161P997_H__ +#include "main.h" +#include "gpios.h" + +#define DAC161P997_IO_PORT (DAC161P997_GPIO_Port) +#define DAC161P997_IO_PIN (DAC161P997_Pin) + +#define DAC161P997_CURRENT_SLOPE 2730.625f // adc = (current / 24) * 0xffff + +// Symbol Periods +#define DUTY_CYCLE_100 (1000U) // (CPU_CLK / BAUD_RATE) +#define DUTY_CYCLE_75 (DUTY_CYCLE_100 * 0.75f) +#define DUTY_CYCLE_50 (DUTY_CYCLE_100 * 0.50f) +#define DUTY_CYCLE_25 (DUTY_CYCLE_100 * 0.25f) + +/************************************************************ + * TI DAC161P997 REGISTER SET ADDRESSES + ************************************************************/ +#define DAC161P997_LCK_REG (0x0000) +#define DAC161P997_CONFIG1_REG (0x0100) +#define DAC161P997_CONFIG2_REG (0x0200) +#define DAC161P997_CONFIG3_REG (0x0300) +#define DAC161P997_ERR_LOW_REG (0x0400) +#define DAC161P997_ERR_HIGH_REG (0x0500) + +// TI DAC161P997 Register Bits +#define DAC161P997_LCK_REG_LOCK (0x00AA) // any value other than 0x95 +#define DAC161P997_LCK_REG_UNLOCK (0x0095) +#define DAC161P997_CONFIG1_REG_RST (0x0001) +#define DAC161P997_CONFIG1_REG_NOP (0 * 0x08u) +#define DAC161P997_CONFIG1_REG_SET_ERR (1 * 0x08u) +#define DAC161P997_CONFIG1_REG_CLEAR_ERR (2 * 0x08u) +#define DAC161P997_CONFIG1_REG_NOP3 (3 * 0x08u) +#define DAC161P997_CONFIG2_REG_LOOP (0x0001) +#define DAC161P997_CONFIG2_REG_CHANNEL (0x0002) +#define DAC161P997_CONFIG2_REG_PARITY (0x0004) +#define DAC161P997_CONFIG2_REG_FRAME (0x0008) +#define DAC161P997_CONFIG2_REG_ACK_EN (0x0010) +#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_16 (0x000F) +#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_8 (0x0007) +#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_1 (0x0000) + +// Tags +#define DACCODE_WRITE (0x00) +#define CONFIG_WRITE (0x01) + +// Valid Symbols +#define ZERO_SYM (0x00) +#define ONE_SYM (0x01) +#define IDLE_SYM (0x02) +#define STATIC_LOW_SYM (0x03) + +// DAC Codes for different currents +#define DACCODE_0mA (0x0000) +#define DACCODE_4mA (0x2AAA) +#define DACCODE_8mA (0x5555) +#define DACCODE_12mA (0x7FFF) +#define DACCODE_16mA (0xAAAA) +#define DACCODE_20mA (0xD555) +#define DACCODE_24mA (0xFFFF) + +typedef enum +{ + DAC161P997_IDLE_SYM, + +} dac161p997_e; +typedef struct +{ + TIM_TypeDef *timer_us; + gpio_t *io; + uint8_t count; +} dac161p997_t; + +void dac161p997_output_0(void); +void dac161p997_output_1(void); +void dac161p997_output_d(void); +void dac161p997_output_symbol(uint8_t sym); +void dac161p997_swif_write_reg(uint16_t data, uint8_t tag); + +extern void dac161p997_output_current(float32 current); +extern void dac161p997_init(TIM_TypeDef *timer_us); +#endif diff --git a/DAC/dac161s997.c b/DAC/dac161s997.c new file mode 100644 index 0000000..645eed4 --- /dev/null +++ b/DAC/dac161s997.c @@ -0,0 +1,56 @@ +#include "dac161s997.h" + + +/** + * @brief 向DAC161S997芯片写入寄存器值 + * + * 该函数通过SPI接口向DAC161S997芯片写入指定的寄存器值。 + * + * @param dac_num DAC芯片编号 + * @param reg 要写入的寄存器地址 + * @param data 要写入的数据,16位 + */ +void dac161s997_write_reg(chip_type_e dac_num, uint8_t reg, uint16_t data) +{ + uint8_t data_buffer[3] = {0, 0, 0}; + data_buffer[0] = reg; + data_buffer[1] = data >> 8; + data_buffer[2] = data; + board_spi_init(dac_num); // 初始化SPI,因为ADC芯片也用SPI1,但是时序不同 + board_spi_cs_on(dac_num); + spi_transmit_receive(&hspi1, data_buffer, 3); + board_spi_cs_off(dac_num); +} + +/** + * @brief 初始化DAC161S997 DAC设备 + */ +void dac161s997_init(void) +{ + dac161s997_write_reg(DAC161S997_1, DAC161S997_ERR_LOW_REG, 0xFFFF); + dac161s997_write_reg(DAC161S997_1, DAC161S997_ERR_CONFIG_REG, 0x070E); + + dac161s997_write_reg(DAC161S997_2, DAC161S997_ERR_LOW_REG, 0xFFFF); + dac161s997_write_reg(DAC161S997_2, DAC161S997_ERR_CONFIG_REG, 0x070E); +} + +/** + * @brief DAC161S997芯片输出函数 + * + * 此函数用于设置DAC161S997芯片的输出电流。 + * + * @param dac_num DAC芯片类型 + * @param current 需要设置的电流值,单位:毫安 + */ +void dac161s997_output(chip_type_e dac_num, float current) +{ + uint32_t dac_code = (uint32_t)((current * 65535.0f) / 24.0f); + uint8_t data_buffer[3] = {0, 0, 0}; + data_buffer[0] = DAC161S997_DACCODE_REG; + data_buffer[1] = dac_code >> 8; + data_buffer[2] = dac_code; + board_spi_init(dac_num); // 初始化SPI,因为ADC芯片也用SPI1,但是时序不同 + board_spi_cs_on(dac_num); + spi_transmit_receive(&hspi1, data_buffer, 3); + board_spi_cs_off(dac_num); +} diff --git a/DAC/dac161s997.h b/DAC/dac161s997.h new file mode 100644 index 0000000..6947e5a --- /dev/null +++ b/DAC/dac161s997.h @@ -0,0 +1,28 @@ +#ifndef __DAC161S997_H__ +#define __DAC161S997_H__ + +#include "main.h" +#include "user_spi.h" +/* Defines ********************************************************************/ +/** + * @defgroup DAC161S997_REGS + * @{ + */ +#define DAC161S997_XFR_REG 0x01 /**< Command: transfer data into register (protected write mode) */ +#define DAC161S997_NOP_REG 0x02 /**< Command: does nothing except resetting the timeout timer */ +#define DAC161S997_PROTECT_REG_WR_REG 0x03 /**< 1 means protected write mode in effect */ +#define DAC161S997_DACCODE_REG 0x04 /**< 16 bit DAC code to be written */ +#define DAC161S997_ERR_CONFIG_REG 0x05 /**< Configuration of error states */ +#define DAC161S997_ERR_LOW_REG 0x06 /**< DAC code for the <4mA alarm level */ +#define DAC161S997_ERR_HIGH_REG 0x07 /**< DAC code for the >20mA alarm level */ +#define DAC161S997_RESET_REG 0x08 /**< Command: reset the chip */ +#define DAC161S997_STATUS_REG 0x09 /**< Status of the chip */ + +#define DAC161S997_SPI SPI1 + +void dac161s997_write_reg(uint8_t dac_num, uint8_t reg, uint16_t data); +uint16_t dac161s997_read(uint8_t reg); +extern void dac161s997_init(void); +extern void dac161s997_output(uint8_t dac_num, float current); + +#endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/epprom/eeprom_fm24.c b/epprom/eeprom_fm24.c new file mode 100644 index 0000000..8cc2bca --- /dev/null +++ b/epprom/eeprom_fm24.c @@ -0,0 +1,227 @@ +/** + * @file eeprom_fm24.c + * @author xxx + * @date 2023-08-29 07:58:27 + * @brief 用于实现FM24 EEPROM相关的读写操作 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#include "eeprom_fm24.h" +#include "delay.h" + +//========在此设定芯片地址============= + +#define W_ADD_COM 0xa0 // 写字节命令及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 0 + +#define R_ADD_COM 0xa1 // 读命令字节及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 1 + +//=======在此设定芯片型号, 1代表24C01; 16代表24C16; 512代表24C512 + +//=======在此设定芯片型号, 1代表24C01; 16代表24C16; 512代表24C512 + +#define FM24_SPI SPI1 + +#define EEPROM_FM24_CS_PORT EE3_CS_GPIO_Port +#define EEPROM_FM24_CS_PIN EE3_CS_Pin +#define EEPROM_FM24_MOSI_PORT SPI_MOSI_GPIO_Port +#define EEPROM_FM24_MOSI_PIN SPI_MOSI_Pin +#define EEPROM_FM24_MISO_PORT SPI_MISO_GPIO_Port +#define EEPROM_FM24_MISO_PIN SPI_MISO_Pin +#define EEPROM_FM24_SCK_PORT SPI_CLK_GPIO_Port +#define EEPROM_FM24_SCK_PIN SPI_CLK_Pin + +static fm24_t _eeprom_fm24; + +void eeprom_fm24_init(void) +{ + spi_gpio_group_t gpios; + spi_normal_config_t cfg; + osel_memset((uint8_t *)&cfg, 0, sizeof(spi_normal_config_t)); + // 创建CS引脚 + gpios.cs = gpio_create(EEPROM_FM24_CS_PORT, EEPROM_FM24_CS_PIN); + gpios.mosi = gpio_create(EEPROM_FM24_MOSI_PORT, EEPROM_FM24_MOSI_PIN); + gpios.sck = gpio_create(EEPROM_FM24_SCK_PORT, EEPROM_FM24_SCK_PIN); + gpios.miso = gpio_create(EEPROM_FM24_MISO_PORT, EEPROM_FM24_MISO_PIN); + gpios.rst = gpio_create(NULL, 0); + gpios.rdy = gpio_create(NULL, 0); + + // 创建SPI对象 + eeprom_fm24_get()->spi = spi_create(SPI_TYPE_NORMAL, gpios, 0); + DBG_ASSERT(eeprom_fm24_get() != NULL __DBG_LINE); + cfg.cmd_rdsr = FM24_CMD_RDSR; + cfg.cmd_wrsr = FM24_CMD_WRSR; + cfg.cmd_wren = FM24_CMD_WREN; + cfg.cmd_wrdi = FM24_CMD_WRDI; + cfg.cmd_write = FM24_CMD_WRITE; + cfg.cmd_read = FM24_CMD_READ; + cfg.dummy_byte = FM24_DUMMY_BYTE; + cfg.address_bytes = 2; + cfg.page_size = FM24_PAGE_SIZE; + cfg.total_size = FM24_SIZE; + cfg.continuous_write = FALSE; + osel_memcpy((uint8_t *)&eeprom_fm24_get()->spi->cfg, (uint8_t *)&cfg, sizeof(spi_normal_config_t)); + // 使能SPI + eeprom_fm24_get()->spi->interface.hardware_enable(eeprom_fm24_get()->spi, FM24_SPI); + // 这里需要复位下SPI,否则读出的数据不对 + eeprom_fm24_get()->spi->interface.u.normal.spi_reset(eeprom_fm24_get()->spi); + + eeprom_fm24_write_protection_close(); + // eeprom_fm24_test(); +} + +fm24_t *eeprom_fm24_get(void) +{ + return &_eeprom_fm24; +} + +void eeprom_fm24_dinit(void) +{ + LL_SPI_Disable(FM24_SPI); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.mosi->port, eeprom_fm24_get()->spi->gpios.mosi->pin); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.miso->port, eeprom_fm24_get()->spi->gpios.miso->pin); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.sck->port, eeprom_fm24_get()->spi->gpios.sck->pin); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.cs->port, eeprom_fm24_get()->spi->gpios.cs->pin); +} + +void eeprom_fm24_enable(void) +{ + uint16_t count = 100; + LL_SPI_Enable(FM24_SPI); + // 判断SPI是否使能成功 + while (LL_SPI_IsEnabled(FM24_SPI) != 1) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +void eeprom_fm24_disable(void) +{ + uint16_t count = 100; + LL_SPI_Disable(FM24_SPI); + // 判断SPI是否关闭成功 + while (LL_SPI_IsEnabled(FM24_SPI) != 0) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +/** + * @brief 关闭EEPROM FM24的写保护 + * + * 此函数用于关闭EEPROM FM24的写保护功能,允许对其进行写操作。 + * + * 调用此函数前,应确保EEPROM FM24的SPI接口已正确初始化。 + */ +void eeprom_fm24_write_protection_close(void) +{ + DBG_ASSERT(eeprom_fm24_get()->spi != NULL __DBG_LINE); + eeprom_fm24_enable(); + eeprom_fm24_get()->spi->interface.u.normal.spi_write_reg(eeprom_fm24_get()->spi, eeprom_fm24_get()->spi->cfg.cmd_wrsr, 0); + eeprom_fm24_disable(); +} + +/** + * @brief 获取EEPROM FM24的写保护状态 + * + * 获取EEPROM FM24的写保护状态。 + * + * @return BOOL 返回TRUE表示没有写保护,返回FALSE表示有写保护 + */ +BOOL eeprom_fm24_write_protection_state(void) +{ + DBG_ASSERT(eeprom_fm24_get()->spi != NULL __DBG_LINE); + eeprom_fm24_enable(); + eeprom_fm24_get()->write_protection.data = eeprom_fm24_get()->spi->interface.u.normal.spi_read_reg(eeprom_fm24_get()->spi, eeprom_fm24_get()->spi->cfg.cmd_rdsr); + eeprom_fm24_disable(); + + if (eeprom_fm24_get()->write_protection.bits.bp0 == 1 || + eeprom_fm24_get()->write_protection.bits.bp1 == 1 || + eeprom_fm24_get()->write_protection.bits.wpen == 1) + { + return FALSE; + } + else + { + return TRUE; + } +} + +BOOL eeprom_fm24_write(uint32_t write_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + // 开启和关闭SPI对读写实时性有影响 + eeprom_fm24_enable(); + ret = eeprom_fm24_get()->spi->interface.u.normal.spi_write(eeprom_fm24_get()->spi, write_addr, data, length); + eeprom_fm24_disable(); + return ret; +} + +BOOL eeprom_fm24_read(uint32_t read_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + // 开启和关闭SPI对读写实时性有影响 + eeprom_fm24_enable(); + ret = eeprom_fm24_get()->spi->interface.u.normal.spi_read(eeprom_fm24_get()->spi, read_addr, data, length); + eeprom_fm24_disable(); + return ret; +} + +BOOL eeprom_fm24_test(void) +{ + const uint8_t buf_size = 5; + uint16_t test_address = FM24_TEST_PAGE * FM24_PAGE_SIZE; + uint8_t buf[buf_size]; + uint8_t rbuf[buf_size]; + osel_memset(buf, 0, buf_size); + osel_memset(rbuf, 0, buf_size); + buf[0] = 0xD5; + buf[1] = 0xC8; + buf[2] = 0x00; + buf[3] = 0x01; + buf[4] = 0x02; + buf[buf_size - 1] = 0xfe; + eeprom_fm24_write(test_address, buf, buf_size); + __NOP(); + eeprom_fm24_read(test_address, rbuf, buf_size); + if (osel_memcmp(buf, rbuf, buf_size) == 0) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 获取FM24 EEPROM的状态 + * + * 获取FM24 EEPROM的当前状态。该函数始终返回TRUE,表示EEPROM正常工作。 + * + * @return BOOL 始终返回TRUE + */ +BOOL eeprom_fm24_status_get(void) +{ + return TRUE; +} diff --git a/epprom/eeprom_fm24.h b/epprom/eeprom_fm24.h new file mode 100644 index 0000000..03cc083 --- /dev/null +++ b/epprom/eeprom_fm24.h @@ -0,0 +1,153 @@ +/** + * @file eeprom_fm24.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief FM24系列EEPROM驱动 https://zhuanlan.zhihu.com/p/598934638 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __EEPROM_FM24_H__ +#define __EEPROM_FM24_H__ +#include "main.h" +#include "spis.h" +/** + * High-endurance 100 trillion (1014) read/writes + * 151-year data retention + * NoDelay™ writes + */ + +#define e2prom 256 // + +#if e2prom == 1 +#define FM24_PAGE_SIZE 16 +#define FM24_SIZE (128 * 8) +#elif e2prom == 2 +#define FM24_PAGE_SIZE 16 +#define FM24_SIZE (256 * 8) +#elif e2prom == 4 +#define FM24_PAGE_SIZE 32 +#define FM24_SIZE (512 * 8) +#elif e2prom == 8 +#define FM24_PAGE_SIZE 64 +#define FM24_SIZE (1024 * 8) +#elif e2prom == 16 +#define FM24_PAGE_SIZE 128 +#define FM24_SIZE (2048 * 8) +#elif e2prom == 32 +#define FM24_PAGE_SIZE 128 +#define FM24_SIZE (4096 * 8) +#elif e2prom == 64 +#define FM24_PAGE_SIZE 256 +#define FM24_SIZE (8192 * 8) +#elif e2prom == 128 +#define FM24_PAGE_SIZE 256 +#define FM24_SIZE (16384) +#elif e2prom == 256 +#define FM24_PAGE_SIZE 256 +#define FM24_SIZE (32768) // 32K 128页 +#define FM24_PAGE_NUM 128 +#elif e2prom == 512 +#define FM24_PAGE_SIZE 512 +#define FM24_SIZE (65536) +#endif + +#define FM24_CMD_RDSR 0x05 /*!< Read Status Register instruction */ +#define FM24_CMD_WRSR 0x01 /*!< Write Status Register instruction */ + +#define FM24_CMD_WREN 0x06 /*!< Write enable instruction */ +#define FM24_CMD_WRDI 0x04 /*!< Write disable instruction */ + +#define FM24_CMD_READ 0x03 /*!< Read from Memory instruction */ +#define FM24_CMD_WRITE 0x02 /*!< Write to Memory instruction */ + +#define FM24_DUMMY_BYTE 0x00 ///< 无用数据 + +#define FM24_TEST_PAGE 0 ///< 测试页地址 + +typedef union +{ + uint8_t data; + struct + { + uint8_t reserve1 : 1; + uint8_t wel : 1; ///< Write enable latch + uint8_t bp0 : 1; ///< Block protect 0 + uint8_t bp1 : 1; ///< Block protect 1 + uint8_t reserve2 : 3; + uint8_t wpen : 1; ///< Write protect enable + } bits; +} fm24_write_protection_u; + +typedef struct +{ + fm24_write_protection_u write_protection; + spi_t *spi; +} fm24_t; + +/** + * @brief Initializes the FM24 EEPROM module. + */ +extern void eeprom_fm24_init(void); + +/** + * @brief Deinitializes the FM24 EEPROM module. + */ +extern void eeprom_fm24_dinit(void); + +/** + * @brief Gets the fm24_t handle of the FM24 EEPROM module. + * @return The fm24_t handle of the FM24 EEPROM module. + */ +extern fm24_t *eeprom_fm24_get(void); + +/** + * @brief Enables the FM24 EEPROM module. + */ +extern void eeprom_fm24_enable(void); + +/** + * @brief Disables the FM24 EEPROM module. + */ +extern void eeprom_fm24_disable(void); + +/** + * @brief Reads data from the FM24 EEPROM module. + * @param read_addr The starting address to read from. + * @param data Pointer to the buffer to store the read data. + * @param length The number of bytes to read. + * @return TRUE if the read operation is successful, FALSE otherwise. + */ +extern BOOL eeprom_fm24_read(uint32_t read_addr, uint8_t *data, uint16_t length); + +/** + * @brief Writes data to the FM24 EEPROM module. + * @param write_addr The starting address to write to. + * @param data Pointer to the data to be written. + * @param length The number of bytes to write. + * @return TRUE if the write operation is successful, FALSE otherwise. + */ +extern BOOL eeprom_fm24_write(uint32_t write_addr, uint8_t *data, uint16_t length); + +/** + * @brief Closes the write protection of the FM24 EEPROM module. + */ +extern void eeprom_fm24_write_protection_close(void); + +/** + * @brief Gets the write protection state of the FM24 EEPROM module. + * @return TRUE if the FM24 EEPROM module is not write-protected, FALSE otherwise. + */ +extern BOOL eeprom_fm24_write_protection_state(void); + +/** + * @brief Performs a test on the FM24 EEPROM module. + */ +extern BOOL eeprom_fm24_test(void); + +/** + * @brief Gets the status of the FM24 EEPROM module. + * @return TRUE if the FM24 EEPROM module is ready, FALSE otherwise. + */ +extern BOOL eeprom_fm24_status_get(void); + +#endif // __EEPROM_FM24_H__ diff --git a/epprom/eeprom_lc02b.c b/epprom/eeprom_lc02b.c new file mode 100644 index 0000000..f0205fc --- /dev/null +++ b/epprom/eeprom_lc02b.c @@ -0,0 +1,172 @@ +#include "eeprom_lc02b.h" +#include "delay.h" +#include "i2cs.h" + +#define W_ADD_COM 0xa8 // 写字节命令及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 0 +#define R_ADD_COM 0xa9 // 读命令字节及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 1 +#define PAGE_SIZE 8U +#define SIZE 256U + +#define EEPROM_LC02B_SDA_PORT I2C1_SDA_GPIO_Port +#define EEPROM_LC02B_SDA_PIN I2C1_SDA_Pin +#define EEPROM_LC02B_SCL_PORT I2C1_SCL_GPIO_Port +#define EEPROM_LC02B_SCL_PIN I2C1_SCL_Pin + +static i2c_t *_eeprom_24lc028bt_i2c; +static TIM_TypeDef *_timer_us; + +static void _delay_us(uint32_t us) +{ + if (_timer_us != NULL) + { + delay_hardware_us(_timer_us, us); + } + else + { + delay_us(us); + } +} + +void eeprom_lc02b_test(void) +{ +#define TEST_SIZE 15 + uint16_t test_address = SIZE - TEST_SIZE; + uint8_t buf[TEST_SIZE]; + for (uint8_t i = 0; i < TEST_SIZE; i++) + { + buf[i] = i + 1; + } + + eeprom_lc02b_write(test_address, buf, TEST_SIZE); + _delay_us(10000); + osel_memset(buf, 0, ARRAY_LEN(buf)); + eeprom_lc02b_read(test_address, buf, TEST_SIZE); + + __NOP(); +} + +/** + * @brief 获取EEPROM LC02B的状态 + * + * 此函数用于获取EEPROM LC02B的当前状态。 + * + * @return 如果EEPROM LC02B处于正常状态,则返回TRUE;否则返回FALSE。 + * 注意:在本实现中,总是返回TRUE,因为这是一个简单的示例函数。 + */ +BOOL eeprom_lc02b_status_get(void) +{ + return TRUE; +} + +/** + * @brief EEPROM LC02B初始化 + * @return {*} + * @note + */ +void eeprom_lc02b_init(TIM_TypeDef *timer_us) +{ + if (timer_us != NULL) + { + _timer_us = timer_us; + ENABLE_TIM_COUNT(_timer_us); + } + + i2c_gpio_group_t gpios; + gpios.scl = gpio_create(EEPROM_LC02B_SCL_PORT, EEPROM_LC02B_SCL_PIN); + gpios.sda = gpio_create(EEPROM_LC02B_SDA_PORT, EEPROM_LC02B_SDA_PIN); + + _eeprom_24lc028bt_i2c = i2c_create(gpios, 10); + // eeprom_lc02b_test(); +} + +/** + * @brief EEPROM LC02B反初始化 + * @return {*} + * @note + */ +void eeprom_lc02b_dinit(void) +{ + GPIO_SET_ANALOG(EEPROM_LC02B_SDA_PORT, EEPROM_LC02B_SDA_PIN); + GPIO_SET_ANALOG(EEPROM_LC02B_SCL_PORT, EEPROM_LC02B_SCL_PIN); +} +/** + * @brief 写入数据 + * @param {uint32_t} write_addr + * @param {uint8_t} *data + * @param {uint16_t} length + * @return {*} + * @note + */ +void eeprom_lc02b_write(uint32_t write_addr, uint8_t *data, uint16_t length) +{ + // 发送开始信号 + _eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c); + // 发送写入地址命令 + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, W_ADD_COM); + // 等待写入地址命令响应 + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + // 发送要写入的地址 + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, (uint8_t)write_addr); + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + + // 循环写入数据 + + for (uint16_t i = 0; i < length; i++) + { + // 写入一个字节数据 + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, *data++); + // 等待响应 + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + write_addr++; + if (write_addr % PAGE_SIZE == 0) + { + _eeprom_24lc028bt_i2c->interface.stop(_eeprom_24lc028bt_i2c); + _delay_us(10000); // 延时10ms,等待写入完成 + _eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c); + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, W_ADD_COM); + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, (uint8_t)write_addr); + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + } + } + // 写入完成,停止I2C总线 + _eeprom_24lc028bt_i2c->interface.stop(_eeprom_24lc028bt_i2c); +} + +/** + * @brief 读取数据 + * @param {uint32_t} read_addr + * @param {uint8_t} *data + * @param {uint16_t} length + * @return {*} + * @note + */ +void eeprom_lc02b_read(uint32_t read_addr, uint8_t *data, uint16_t length) +{ + // 发送开始信号 + _eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c); + // 发送写入地址命令 + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, W_ADD_COM); + // 等待写入地址命令响应 + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + + // 发送要读取的地址 + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, (uint8_t)read_addr); + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + + // 发送开始信号 + _eeprom_24lc028bt_i2c->interface.start(_eeprom_24lc028bt_i2c); + // 发送读取地址命令 + _eeprom_24lc028bt_i2c->interface.write_byte(_eeprom_24lc028bt_i2c, R_ADD_COM); + // 等待读取地址命令响应 + _eeprom_24lc028bt_i2c->interface.wait_ack(_eeprom_24lc028bt_i2c); + // 循环读取数据 + for (uint16_t i = 0; i < length - 1; i++) + { + // 读取一个字节数据 + *data++ = _eeprom_24lc028bt_i2c->interface.read_byte(_eeprom_24lc028bt_i2c, TRUE); + } + *data++ = _eeprom_24lc028bt_i2c->interface.read_byte(_eeprom_24lc028bt_i2c, FALSE); + // 停止I2C总线 + _eeprom_24lc028bt_i2c->interface.stop(_eeprom_24lc028bt_i2c); +} diff --git a/epprom/eeprom_lc02b.h b/epprom/eeprom_lc02b.h new file mode 100644 index 0000000..ec181af --- /dev/null +++ b/epprom/eeprom_lc02b.h @@ -0,0 +1,60 @@ +/** + * @file eeprom_lc02b.h + * @author xxx + * @date 2023-12-27 14:44:02 + * @brief + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __EEPROM_LC02B_H +#define __EEPROM_LC02B_H +#include "main.h" + +/** + * is a 2 Kbit Electrically Erasable PROM. The LC02B EEPROM is organized as 256 x 8 bits. 256 bytes + * Data Retention >200 Years + * More than 1 Million Erase/Write Cycles + * Page Write Time 3 ms, typical + */ + +/** + * @brief Initializes the LC02B EEPROM module. + */ +void eeprom_lc02b_init(TIM_TypeDef *timer_us); + +/** + * @brief Deinitializes the LC02B EEPROM module. + */ +void eeprom_lc02b_dinit(void); + +/** + * @brief Writes data to the LC02B EEPROM module. + * + * @param write_addr The starting address to write the data. + * @param data The pointer to the data to be written. + * @param length The length of the data to be written. + */ +void eeprom_lc02b_write(uint32_t write_addr, uint8_t *data, uint16_t length); + +/** + * @brief Reads data from the LC02B EEPROM module. + * + * @param read_addr The starting address to read the data. + * @param data The pointer to store the read data. + * @param length The length of the data to be read. + */ +void eeprom_lc02b_read(uint32_t read_addr, uint8_t *data, uint16_t length); + +/** + * @brief Performs a test on the LC02B EEPROM module. + */ +void eeprom_lc02b_test(void); + +/** + * @brief Gets the LC02B EEPROM status. + * + * This function is used to get the current status of the LC02B EEPROM. + * + * @return TRUE if the LC02B EEPROM is in normal status, FALSE otherwise. + */ +BOOL eeprom_lc02b_status_get(void); +#endif ///< !__EEPROM_LC02B_H diff --git a/epprom/eeprom_m95.c b/epprom/eeprom_m95.c new file mode 100644 index 0000000..bc1028c --- /dev/null +++ b/epprom/eeprom_m95.c @@ -0,0 +1,330 @@ +/** + * @file eeprom_m95.c + * @author xxx + * @date 2023-08-30 08:58:43 + * @brief 用于实现M95 EEPROM相关的读写操作 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "eeprom_m95.h" +#include "spis.h" +#include "delay.h" +#include "storage.h" + +#define M95_SPI SPI1 + +#define EEPROM_M95_1_CS_PORT EE1_CS_GPIO_Port +#define EEPROM_M95_1_CS_PIN EE1_CS_Pin +#define EEPROM_M95_2_CS_PORT EE2_CS_GPIO_Port +#define EEPROM_M95_2_CS_PIN EE2_CS_Pin + +// 下面宏定义为2个EEPROM_M95的引脚定义 +#define EEPROM_M95_MOSI_PORT SPI_MOSI_GPIO_Port +#define EEPROM_M95_MOSI_PIN SPI_MOSI_Pin +#define EEPROM_M95_MISO_PORT SPI_MISO_GPIO_Port +#define EEPROM_M95_MISO_PIN SPI_MISO_Pin +#define EEPROM_M95_SCK_PORT SPI_CLK_GPIO_Port +#define EEPROM_M95_SCK_PIN SPI_CLK_Pin + +#define M95_TEST_PAGE (M95_PAGE_SIZE_256 - 1) ///< 测试页地址 + +m95_number_t eeprom_m95s[M95_MAX]; + +/** + * @brief 初始化EEPROM_M95eeprom_m95s + * @param {m95_number_e} num + * @return {*} + * @note 初始化函数对板卡上不同的芯片定义了块大小 + */ +void eeprom_m95_init(m95_number_e num) +{ + DBG_ASSERT(num < M95_MAX __DBG_LINE); + spi_gpio_group_t gpios; + spi_t *eeprom_m95_spi; + spi_normal_config_t cfg; + osel_memset((uint8_t *)&cfg, 0, sizeof(spi_normal_config_t)); + cfg.cmd_rdsr = M95_CMD_RDSR; + cfg.cmd_wrsr = M95_CMD_WRSR; + cfg.cmd_wren = M95_CMD_WREN; + cfg.cmd_wrdi = M95_CMD_WRDI; + cfg.cmd_write = M95_CMD_WRITE; + cfg.cmd_read = M95_CMD_READ; + cfg.dummy_byte = M95_DUMMY_BYTE; + cfg.continuous_write = FALSE; + // 128 byte + if (num == M95_1) + { + // 创建CS引脚 + gpios.cs = gpio_create(EEPROM_M95_1_CS_PORT, EEPROM_M95_1_CS_PIN); + + cfg.address_bytes = 3; + cfg.page_size = M95_PAGE_SIZE_256; + cfg.total_size = _M95M02_; + } + // 256 byte + else if (num == M95_2) + { + // 创建CS引脚 + gpios.cs = gpio_create(EEPROM_M95_2_CS_PORT, EEPROM_M95_2_CS_PIN); + + cfg.address_bytes = 3; + cfg.page_size = M95_PAGE_SIZE_256; + cfg.total_size = _M95M02_; + } + else + { + DBG_ASSERT(FALSE __DBG_LINE); + } + + gpios.mosi = gpio_create(EEPROM_M95_MOSI_PORT, EEPROM_M95_MOSI_PIN); + gpios.sck = gpio_create(EEPROM_M95_SCK_PORT, EEPROM_M95_SCK_PIN); + gpios.miso = gpio_create(EEPROM_M95_MISO_PORT, EEPROM_M95_MISO_PIN); + gpios.rst = gpio_create(NULL, 0); + gpios.rdy = gpio_create(NULL, 0); + + // 创建SPI对象 + eeprom_m95_spi = spi_create(SPI_TYPE_NORMAL, gpios, 10); + DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); + osel_memcpy((uint8_t *)&eeprom_m95_spi->cfg, (uint8_t *)&cfg, sizeof(spi_normal_config_t)); + // 使能SPI + eeprom_m95_spi->interface.hardware_enable(eeprom_m95_spi, M95_SPI); + eeprom_m95s[num].num = num; + eeprom_m95s[num].spi = eeprom_m95_spi; + // 这里需要设置,否则读出的数据不对 + eeprom_m95_spi->interface.u.normal.spi_reset(eeprom_m95_spi); + eeprom_m95_write_protection_close(num); // 关闭写保护 + + // eeprom_m95_test(num); +} + +/** + * @brief 反初始化EEPROM_M95 + * @param {m95_number_e} num + * @return {*} + * @note + */ +void eeprom_m95_dinit(m95_number_e num) +{ + LL_SPI_Disable(M95_SPI); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.mosi->port, eeprom_m95s[num].spi->gpios.mosi->pin); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.miso->port, eeprom_m95s[num].spi->gpios.miso->pin); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.sck->port, eeprom_m95s[num].spi->gpios.sck->pin); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.cs->port, eeprom_m95s[num].spi->gpios.cs->pin); +} + +/** + * @brief M95 EEPROM使能 + * @return {*} + * @note + */ +void eeprom_m95_enable(void) +{ + uint16_t count = 100; + LL_SPI_Enable(M95_SPI); + // 判断SPI是否使能成功 + while (LL_SPI_IsEnabled(M95_SPI) != 1) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +/** + * @brief M95 EEPROM失能 + * @return {*} + * @note + */ +void eeprom_m95_disable(void) +{ + uint16_t count = 100; + LL_SPI_Disable(M95_SPI); + // 判断SPI是否关闭成功 + while (LL_SPI_IsEnabled(M95_SPI) != 0) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +/** + * @brief 关闭EEPROM M95写保护 + * + * 关闭指定M95 EEPROM的写保护功能。 + * + * @param num 指定M95 EEPROM的编号 + */ +void eeprom_m95_write_protection_close(m95_number_e num) +{ + spi_t *handle = eeprom_m95s[num].spi; + DBG_ASSERT(handle != NULL __DBG_LINE); + eeprom_m95_enable(); + handle->interface.u.normal.spi_write_reg(handle, handle->cfg.cmd_wrsr, 0); + eeprom_m95_disable(); +} + +/** + * @brief 获取M95 EEPROM写保护状态(软件) + * + * 根据传入的M95编号获取其写保护状态。 + * + * @param num M95编号 + * @return 如果M95 EEPROM处于写保护状态,则返回FALSE;否则返回TRUE。 + */ +BOOL eeprom_m95_write_protection_state(m95_number_e num) +{ + spi_t *handle = eeprom_m95s[num].spi; + DBG_ASSERT(handle != NULL __DBG_LINE); + eeprom_m95_enable(); + eeprom_m95s[num].write_protection.data = handle->interface.u.normal.spi_read_reg(handle, handle->cfg.cmd_rdsr); + eeprom_m95_disable(); + + if (eeprom_m95s[num].write_protection.bits.bp0 == 1 || + eeprom_m95s[num].write_protection.bits.bp1 == 1 || + eeprom_m95s[num].write_protection.bits.srwd == 1) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief 读取M95 EEPROM内存数据 + * @param num EEPROM模块编号(0或1) + * @param read_addr 要读取的地址 + * @param data 存储读取数据的缓冲区 + * @param length 要读取的数据长度 + * @return {*} + */ +BOOL eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + + spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; // 获取EEPROM模块的SPI配置 + DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); + // 开启和关闭SPI对读写实时性有影响 + eeprom_m95_enable(); + ret = eeprom_m95_spi->interface.u.normal.spi_read(eeprom_m95_spi, read_addr, data, length); + eeprom_m95_disable(); + return ret; +} + +/** + * @brief 向M95 EEPROM内存写入数据 + * @param num EEPROM模块编号(0或1) + * @param write_addr 要写入的地址 + * @param data 包含要写入数据的缓冲区 + * @param length 要写入的数据长度 + * @return {*} + */ +BOOL eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; + DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); + // 开启和关闭SPI对读写实时性有影响 + eeprom_m95_enable(); + ret = eeprom_m95_spi->interface.u.normal.spi_write(eeprom_m95_spi, write_addr, data, length); + eeprom_m95_disable(); + return ret; +} + +BOOL eeprom_m95_1_read(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_read(M95_1, addr, buf, size); +} + +BOOL eeprom_m95_1_write(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_write(M95_1, addr, (uint8_t *)buf, size); +} + +BOOL eeprom_m95_2_read(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_read(M95_2, addr, buf, size); +} + +BOOL eeprom_m95_2_write(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_write(M95_2, addr, (uint8_t *)buf, size); +} +/** + * @brief 用于M95 EEPROM测试 + * @param {m95_number_e} num + * @return {*} + * @note + */ +BOOL eeprom_m95_test(m95_number_e num) +{ + const uint8_t buf_size = 5; + storage_t *st = storage_init(M95_TEST_PAGE * M95_PAGE_SIZE_256, M95_PAGE_SIZE_256); + DBG_ASSERT(st != NULL __DBG_LINE); + + if (num == M95_1) + { + st->ops.read = eeprom_m95_1_read; + st->ops.write = eeprom_m95_1_write; + } + else + { + st->ops.read = eeprom_m95_2_read; + st->ops.write = eeprom_m95_2_write; + } + uint8_t buf[buf_size]; + uint8_t rbuf[buf_size]; + storage_add_node(st, 0, buf_size); + osel_memset(buf, 0, buf_size); + buf[0] = 0xD5; + buf[1] = 0xC8; + buf[2] = num; + buf[3] = 0xaa; + buf[4] = 0xbb; + storage_write(st, 0, buf); + __NOP(); + storage_read(st, 0, rbuf); + storage_destroy(st); + if (osel_memcmp(buf, rbuf, buf_size) == 0) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 获取EEPROM M95状态 + * + * 获取EEPROM M95设备的工作状态。 + * + * @param num EEPROM M95设备编号 + * + * @return 如果EEPROM M95设备正常工作,返回TRUE;否则返回FALSE + */ +BOOL eeprom_m95_status_get(m95_number_e num) +{ + return TRUE; +} diff --git a/epprom/eeprom_m95.h b/epprom/eeprom_m95.h new file mode 100644 index 0000000..45158b4 --- /dev/null +++ b/epprom/eeprom_m95.h @@ -0,0 +1,168 @@ +/** + * @file eeprom_m95.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief 头文件 eeprom_m95.h + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __EEPROM_M95_H +#define __EEPROM_M95_H +#include "main.h" +#include "spis.h" + +/** + * Byte Write within 10 ms + * More than 4 million Write cycles + * More than 200-year data retention + */ + +#define _M95010_ 128 +#define _M95020_ 256 +#define _M95040_ 512 + +#define _M95080_ 1024 +#define _M95160_ 2048 +#define _M95320_ 4096 +#define _M95640_ 8192 + +#define _M95128_ 16384 +#define _M95256_ 32768 + +#define _M95512_ 65536 ///< 65K +#define _M95M02_ 262144 ///< 256K 262144/256 = 1024 + +#define M95_CMD_RDSR 0x05 /*!< Read Status Register instruction */ +#define M95_CMD_WRSR 0x01 /*!< Write Status Register instruction */ + +#define M95_CMD_WREN 0x06 /*!< Write enable instruction */ +#define M95_CMD_WRDI 0x04 /*!< Write disable instruction */ + +#define M95_CMD_READ 0x03 /*!< Read from Memory instruction */ +#define M95_CMD_WRITE 0x02 /*!< Write to Memory instruction */ + +///< Instruction available only for the M95_2-D device. +#define M95_CMD_RDID 0x83 /*!< Read identification page*/ +#define M95_CMD_WRID 0x82 /*!< Write identification page*/ +#define M95_CMD_RDLS 0x83 /*!< Reads the Identification page lock status*/ +#define M95_CMD_LID 0x82 /*!< Locks the Identification page in read-only mode*/ + +#define M95_DUMMY_BYTE 0xA5 ///< 虚拟字节 + +///< 定义存储器大小(Bytes) +typedef enum +{ + M95_PAGE_SIZE_16 = 16, ///< _M95010_ 、_M95020_ 、_M95040_ + M95_PAGE_SIZE_32 = 32, ///< _M95080_ 、_M95160_、_M95320_、_M95640_ + M95_PAGE_SIZE_64 = 64, ///< _M95128_、_M95256_ + M95_PAGE_SIZE_128 = 128, ///< _M95512_ + M95_PAGE_SIZE_256 = 256, ///< _M95M02_ +} m95_page_size_e; + +typedef enum +{ + M95_1, + M95_2, + M95_MAX, +} m95_number_e; ///< 板卡上2块m95芯片定义 + +typedef union +{ + uint8_t data; + struct + { + uint8_t wip : 1; ///< Write in progress + uint8_t wel : 1; ///< Write enable latch + uint8_t bp0 : 1; ///< Block protect 0 + uint8_t bp1 : 1; ///< Block protect 1 + uint8_t reserve : 3; + uint8_t srwd : 1; ///< Status register write protect + } bits; +} m95_write_protection_u; + +typedef struct +{ + m95_number_e num; + m95_write_protection_u write_protection; + spi_t *spi; +} m95_number_t; + +extern m95_number_t eeprom_m95s[M95_MAX]; ///< m95芯片数组 + +/** + * @brief Initializes the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern void eeprom_m95_init(m95_number_e num); + +/** + * @brief Deinitializes the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern void eeprom_m95_dinit(m95_number_e num); + +/** + * @brief Enables the M95 EEPROM module. + */ +extern void eeprom_m95_enable(void); + +/** + * @brief Disables the M95 EEPROM module. + */ +extern void eeprom_m95_disable(void); + +/** + * @brief Closes the write protection of the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern void eeprom_m95_write_protection_close(m95_number_e num); + +/** + * @brief write protection state of the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern BOOL eeprom_m95_write_protection_state(m95_number_e num); + +/** + * @brief Reads data from the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + * @param read_addr The address to read from. + * @param data The buffer to store the read data. + * @param length The number of bytes to read. + */ +extern BOOL eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length); + +/** + * @brief Writes data to the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + * @param write_addr The address to write to. + * @param data The data to write. + * @param length The number of bytes to write. + */ +extern BOOL eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length); + +/** + * @brief Performs a test on the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern BOOL eeprom_m95_test(m95_number_e num); + +/** + * @brief Gets the status of the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern BOOL eeprom_m95_status_get(m95_number_e num); + +extern BOOL eeprom_m95_1_read(uint32_t addr, uint8_t *buf, uint16_t size); +extern BOOL eeprom_m95_1_write(uint32_t addr, uint8_t *buf, uint16_t size); +extern BOOL eeprom_m95_2_read(uint32_t addr, uint8_t *buf, uint16_t size); +extern BOOL eeprom_m95_2_write(uint32_t addr, uint8_t *buf, uint16_t size); +#endif ///< __EEPROM_M95_H diff --git a/gp8302.c b/gp8302.c new file mode 100644 index 0000000..bc98904 --- /dev/null +++ b/gp8302.c @@ -0,0 +1,68 @@ +#include "gp8302.h" + +#define I2C_GP8302_1_SCL_PORT I2C2_SCL_GPIO_Port +#define I2C_GP8302_1_SCL_PIN I2C2_SCL_Pin +#define I2C_GP8302_1_SDA_PORT I2C2_SDA_GPIO_Port +#define I2C_GP8302_1_PIN I2C2_SDA_Pin +#define I2C_GP8302_1_DETECTION_PORT ALARM1_GPIO_Port +#define I2C_GP8302_1_DETECTION_PIN ALARM1_Pin + +#define I2C_GP8302_2_SCL_PORT I2C3_SCL_GPIO_Port +#define I2C_GP8302_2_SCL_PIN I2C3_SCL_Pin +#define I2C_GP8302_2_SDA_PORT I2C3_SDA_GPIO_Port +#define I2C_GP8302_2_PIN I2C3_SDA_Pin +#define I2C_GP8302_2_DETECTION_PORT ALARM2_GPIO_Port +#define I2C_GP8302_2_DETECTION_PIN ALARM2_Pin + +static gp8302_t gp8302[GP8302_MAX]; + +void gp8302_init(void) +{ + i2c_gpio_group_t gpios; + gpios.scl = gpio_create(I2C_GP8302_1_SCL_PORT, I2C_GP8302_1_SCL_PIN); + gpios.sda = gpio_create(I2C_GP8302_1_SDA_PORT, I2C_GP8302_1_PIN); + gp8302[GP8302_1].i2c = i2c_create(gpios, 0); + + gpios.scl = gpio_create(I2C_GP8302_2_SCL_PORT, I2C_GP8302_2_SCL_PIN); + gpios.sda = gpio_create(I2C_GP8302_2_SDA_PORT, I2C_GP8302_2_PIN); + gp8302[GP8302_2].i2c = i2c_create(gpios, 0); +} + +BOOL gp8302_detection(void) +{ + BOOL res = FALSE; + if (GPIO_READ(I2C_GP8302_1_DETECTION_PORT, I2C_GP8302_1_DETECTION_PIN) == 0) + { + res = TRUE; + } + else + { + return FALSE; + } + + if (GPIO_READ(I2C_GP8302_2_DETECTION_PORT, I2C_GP8302_2_DETECTION_PIN) == 0) + { + res = TRUE; + } + else + { + return FALSE; + } + return res; +} + +void gp8302_output(gp8302_number_e no, uint16_t pressure) +{ + uint16_t hi = ((pressure >> 8) << 4) + ((uint8_t)pressure >> 4); + uint8_t lo = ((uint8_t)pressure) << 4; + pressure = (hi << 8) + lo; + i2c_t *p = gp8302[no].i2c; + p->interface.start(p); + p->interface.write_byte(p, 0xb0); + p->interface.wait_ack(p); + p->interface.write_byte(p, 2); + p->interface.wait_ack(p); + p->interface.write_word(p, pressure); + p->interface.wait_ack(p); + p->interface.stop(p); +} diff --git a/gp8302.h b/gp8302.h new file mode 100644 index 0000000..9540823 --- /dev/null +++ b/gp8302.h @@ -0,0 +1,22 @@ +#ifndef __GP8302_H__ +#define __GP8302_H__ +#include "main.h" +#include "i2cs.h" +#include "lib.h" + +typedef enum +{ + GP8302_1, + GP8302_2, + GP8302_MAX, +} gp8302_number_e; + +typedef struct +{ + i2c_t *i2c; +} gp8302_t; + +void gp8302_init(void); +BOOL gp8302_detection(void); +void gp8302_output(gp8302_number_e no, uint16_t pressure); +#endif diff --git a/ntc_3950.c b/ntc_3950.c new file mode 100644 index 0000000..35a30ba --- /dev/null +++ b/ntc_3950.c @@ -0,0 +1,118 @@ +/** + * @file ntc_3950.c + * @author xxx + * @date 2023-08-30 08:58:43 + * @brief 用于实现NTC的应用功能 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "ntc_3950.h" +#define CPU_VREF 2.5 +#define NTC_VREF 2.5 +#define TABLE_SIZE 185 +#define NTC_SERIES_RESISTOR 200000 ///< 200K +#define TEMP_MIN -55 +#define TEMP_MAX 129 + +const uint32_t _table[TABLE_SIZE] = { + 8989000, 8242680, 7592960, 7021380, 6513750, // -55,-54,-53,-52,-51 + 6059060, 5648680, 5275800, 4935020, 4621990, 4333220, 4065840, 3817520, 3586310, 3370600, // -50,-49,-48,-47,-46,-45,-44,-43,-42,-41 + 3169000, 2980330, 2803600, 2637910, 2482470, 2336580, 2199620, 2071020, 1950230, 1836790, // -40,-39,-38,-37,-36,-35,-34,-33,-32,-31 + 1730230, 1630150, 1536140, 1447840, 1364900, 1287000, 1213820, 1145090, 1080530, 1019890, // -30,-29,-28,-27,-26,-25,-24,-23,-22,-21 + 962912, 909379, 859074, 811797, 767359, 725581, 686296, 649348, 614590, 581883, // -20,-19,-18,-17,-16,-15,-14,-13,-12,-11 + 551100, 522117, 494824, 469113, 444886, 422050, 400518, 380209, 361048, 342963, // -10,-9,-8,-7,-6,-5,-4,-3,-2,-1 + 326560, 309764, 294529, 280131, 266520, 253647, 241470, 229946, 219036, 208706, // 0,1, 2, 3,4,5,6,7,8,9 + 198920, 189647, 180857, 172523, 164618, 157118, 150000, 143243, 136827, 130731, // 10,11 ,12, 13,14,15,16,17,18,19 + 124940, 119435, 114202, 109225, 104491, 100000, 95699, 91617, 87731, 84028, // 20,21, 22, 23,24,25,26,27,28,29 + 80501, 77140, 73936, 70881, 67968, 65188, 62537, 60006, 57590, 55283, // 30,31, 32, 33,34,35,36,37,38,39 + 53080, 50976, 48965, 47044, 45207, 43451, 41771, 40165, 38628, 37157, // 40,41, 42, 43,34,35,36,37,38,39 + 35750, 34402, 33112, 31876, 30692, 29558, 28471, 27429, 26430, 25472, // 50,51, 52, 53,54,55,56,57,58,59 + 24554, 23672, 22827, 22016, 21237, 20489, 19771, 19082, 18420, 17784, // 60,61, 62, 63,64,65,66,67,68,69 + 17172, 16585, 16020, 15477, 14955, 14453, 13970, 13505, 13058, 12628, // 70,71, 72, 73,74,75,76,77,78,79 + 12213, 11815, 11431, 11061, 10705, 10362, 10031, 9712, 9405, 9110, // 80,81, 82, 83,84,85,86,87,88,89 + 8824, 8549, 8284, 8028, 7782, 7544, 7314, 7093, 6879, 6673, // 90,91, 92, 93,94,95,96,97,98,99 + 6474, 6281, 6096, 5916, 5743, 5576, 5415, 5259, 5108, 4963, // 101,102, 103,104,105,106,107,108,109 + 4822, 4687, 4555, 4428, 4306, 4187, 4073, 3962, 3855, 3751, // 111,112, 113,114,115,116,117,118,119 + 3651, 3555, 3461, 3371, 3283, 3199, 3100, 3099, 2899, 2799, // 121,122, 123,124,125,126,127,128,129 +}; + +/** + * @brief 温度查表 + * @param {uint32_t} *list + * @param {uint32_t} rt + * @return {*} + * @note + */ +static uint8_t ntc_lookup(const uint32_t *list, uint32_t rt) +{ + uint8_t middle = 0; + uint8_t indexL = 0; + uint8_t indexR = TABLE_SIZE - 1; + if (rt >= *(list + 0)) + return 0; + if (rt <= *(list + TABLE_SIZE - 1)) + return TABLE_SIZE - 1; + + while ((indexR - indexL) > 1) + { + middle = (indexL + indexR) >> 1; + if (rt == *(list + middle)) + return middle; + else if (rt > *(list + middle)) + indexR = middle; + else if (rt < *(list + middle)) + indexL = middle; + } + return indexL; +} + +/** + * @brief 获取温度值,单位为0.1摄氏度 + * @param {uint16_t} adc采集值 + * @return {float32_u} 温度值 + * @note + */ +float32_u ntc_get_temp(uint16_t adc) +{ + + uint8_t index = 0; + int16_t data = 0; + int16_t t = 0; + int16_t result = 0; + uint32_t rt = 0; + const int16_t base = TEMP_MIN * 10; + float32_u res; + res.f = TEMP_MIN; + + /** + * ad = (4095*rt)/(rt+10000) + * + * rt = (ad*NTC_SERIES_RESISTOR*CPU_VREF)/(NTC_VREF*4095-CPU_VREF*ad) + */ + rt = (adc * NTC_SERIES_RESISTOR * CPU_VREF) / (NTC_VREF * 4095 - CPU_VREF * adc); + index = ntc_lookup(_table, rt); + if (rt >= _table[0]) + return res; + if (rt <= *(_table + TABLE_SIZE - 1)) + { + result = (TABLE_SIZE - 1) * 10 + base; + } + else + { + data = _table[index] - _table[index + 1]; + t = 10 * (_table[index] - rt) / data; + result = base + index * 10 + t; + } + res.f = result / 10.0; + return res; +} + +// 判断传感器是否故障 +BOOL ntc_is_fault_temp(float32 temp) +{ + if (temp < TEMP_MIN || temp > TEMP_MAX) + { + return TRUE; + } + return FALSE; +} diff --git a/ntc_3950.h b/ntc_3950.h new file mode 100644 index 0000000..efd52c0 --- /dev/null +++ b/ntc_3950.h @@ -0,0 +1,15 @@ +/** + * @file ntc_3950.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief 头文件 ntc_3950.h + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __NTC_B3950_H__ +#define __NTC_B3950_H__ +#include "main.h" + +extern float32_u ntc_get_temp(uint16_t adc); ///< 获取温度值 +extern BOOL ntc_is_fault_temp(float32 temp); ///< 判断传感器是否故障 +#endif ///< __NTC_B3950_H__ diff --git a/pt100.c b/pt100.c new file mode 100644 index 0000000..daa6af9 --- /dev/null +++ b/pt100.c @@ -0,0 +1,203 @@ +#include "pt100.h" +#pragma pack(1) +typedef struct +{ + uint8_t address; // 从机地址 + uint8_t code; // 功能码 + uint16_t reg_address; // 寄存器地址 + uint16_t reg_num; // 寄存器数量 + uint16_t crc; +} pt100_data_t; +#pragma pack() + +extern uart_t *uarts[UART_NUM_MAX]; +static pt100_t handle; +float32 pt100_tempture[PT100_DATA_CHANNEL_NUM]; +/* CRC low byte table */ +static const unsigned char auchCRCLo[] = + { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40}; + +/* CRC high byte table */ +static const unsigned char auchCRCHi[] = + { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40}; + +/* Public functions ----------------------------------------------------------*/ + +/** + * @brief Calculate Modbus CRC16 + * @param puchMsg: Data to calculate CRC + * @param usMsgLen: Data length + * @param puchCRCHi: High byte of CRC value + * @param puchCRCLo: Low byte of CRC value + * @note Send the low byte first + * @retval None + */ +void GetModbusCRC16(unsigned char *puchMsg, unsigned short usMsgLen, + unsigned char *puchCRCLo, unsigned char *puchCRCHi) +{ + unsigned char uchCRCLo = 0xFF; + unsigned char uchCRCHi = 0xFF; + unsigned short uIndex = 0; + + while (usMsgLen--) + { + uIndex = (unsigned char)(uchCRCHi ^ *puchMsg++); + uchCRCHi = (unsigned char)(uchCRCLo ^ auchCRCHi[uIndex]); + uchCRCLo = (unsigned char)(auchCRCLo[uIndex]); + } + + *puchCRCLo = uchCRCHi; + *puchCRCHi = uchCRCLo; +} + +static void pt100_rx_cb(uint8_t uart_index, uint8_t *data, uint16_t len) +{ + uart_t *h = handle.huart; + if (h != NULL) + { + if (h->uart_index == uart_index) + { + if ((h->rx_sta & 0x8000) == 0) // 接收未完成 + { + handle.ctx_recv_buf[h->rx_sta & 0X3FFF] = h->rxbuf[0]; + h->rx_sta++; + if (h->rx_sta > (PT100_MODBUS_RECV_LENGTH - 1)) + h->rx_sta = 0; // 接收数据错误,重新开始接收 + } + } + } +} + +static void pt100_uart_init(void) +{ + if (uarts[PT100_RS485_PORT] == NULL) + { + uarts[PT100_RS485_PORT] = uart_create(USART4, FALSE, 1, pt100_rx_cb, FALSE, 0, NULL); + uarts[PT100_RS485_PORT]->uart_index = PT100_RS485_PORT; + } +} + +void pt100_init(send_data_cb_t cb) +{ + handle.send_data_cb = cb; + pt100_uart_init(); + handle.huart = uarts[PT100_RS485_PORT]; + DBG_ASSERT(handle.huart != NULL __DBG_LINE); + GPIO_RESET(RS485_EN2_GPIO_Port, RS485_EN2_Pin); + uart_recv_en(handle.huart); + handle.idel_flag = false; + osel_memset((uint8_t *)pt100_tempture, 0, sizeof(float32) * PT100_DATA_CHANNEL_NUM); +} + +void pt100_process(void) +{ + uart_t *h = handle.huart; + DBG_ASSERT(h != NULL __DBG_LINE); + pt100_data_t data; + data.address = 0x01; + data.code = 0x04; + + data.reg_address = 0x0000; + data.reg_address = S2B_UINT16(data.reg_address); + + data.reg_num = PT100_DATA_CHANNEL_NUM; + data.reg_num = S2B_UINT16(data.reg_num); + GetModbusCRC16((uint8_t *)&data, sizeof(pt100_data_t) - 2, (uint8_t *)&data.crc, (uint8_t *)&data.crc + 1); + + h->rx_sta = 0; + handle.send_data_cb((uart_num_e)h->uart_index, (uint8_t *)&data, sizeof(pt100_data_t)); +} + +void pt100_data_deal(void) +{ + uart_t *h = handle.huart; + uint8_t *p = handle.ctx_recv_buf; + uint8_t offset = 0; + uint16_t crc = 0, crc1 = 0; + uint16_t length = h->rx_sta; + uint16_t pt100_data[PT100_DATA_CHANNEL_NUM]; + uint8_t pt100_data_length = 0; + if (h->rx_sta == 0) + { + return; // 未收到数据切换到下一个设备 + } + h->rx_sta |= 0x8000; // 暂停串口接收数据,20ms预留了充足的接收时间 + + if (p[offset] == 0x01 && p[++offset] == 0x04 && length > 4) + { + offset++; + // CRC校验 + GetModbusCRC16(p, length - 2, (uint8_t *)&crc, (uint8_t *)&crc + 1); + osel_memcpy((uint8_t *)&crc1, p + length - 2, 2); + if (crc == crc1) + { + pt100_data_length = p[offset]; + offset++; // 偏移过长度 + osel_memcpy((uint8_t *)pt100_data, p + offset, pt100_data_length); + for (uint8_t i = 0; i < PT100_DATA_CHANNEL_NUM; i++) + { + pt100_data[i] = B2S_UINT16(pt100_data[i]); + if (pt100_data[i] != 0xffff) + { + pt100_tempture[i] = (float32)pt100_data[i] / 10; + } + else + { + pt100_tempture[i] = 129; + } + } + } + } +} diff --git a/pt100.h b/pt100.h new file mode 100644 index 0000000..d384837 --- /dev/null +++ b/pt100.h @@ -0,0 +1,28 @@ +#ifndef __PT100_H__ +#define __PT100_H__ +#include "board.h" +#include "agile_modbus.h" +#define PT100_RS485_PORT UART_NUM_3 + +#define PT100_SLAVER_ADDR 1U // 从机地址 +#define PT100_DATA_CHANNEL_NUM 3U // 通道数 +#define PT100_MODBUS_RECV_LENGTH 30U // 接收缓冲区大小 +#define PT100_MODBUS_SEND_LENGTH 30U // modbus最大数据长度 + +typedef struct +{ + BOOL idel_flag; // 通过这个标识位来判断是否需要初始化modbus + uart_t *huart; + float32_t temperature; // 温度 + + agile_modbus_rtu_t ctx_rtu; + uint8_t ctx_send_buf[PT100_MODBUS_SEND_LENGTH]; + uint8_t ctx_recv_buf[PT100_MODBUS_RECV_LENGTH]; // 公用一个接收缓冲区 + send_data_cb_t send_data_cb; // 发送数据回调函数,外部传入 +} pt100_t; + +extern float32 pt100_tempture[PT100_DATA_CHANNEL_NUM]; +void pt100_init(send_data_cb_t cb); +void pt100_process(void); +void pt100_data_deal(void); +#endif // __PT100_H__ diff --git a/rtc_rx8010.c b/rtc_rx8010.c new file mode 100644 index 0000000..ec34508 --- /dev/null +++ b/rtc_rx8010.c @@ -0,0 +1,560 @@ +/** + * @file rtc_rx8010.c + * @author xxx + * @date 2023-08-30 08:58:43 + * @brief 用于实现RTC芯片RX8010的应用功能 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "rtc_rx8010.h" +#include "i2cs.h" +#include "delay.h" + +#define RTC_RX8010_SDA_PORT RTC_SDA_GPIO_Port +#define RTC_RX8010_SDA_PIN RTC_SDA_Pin +#define RTC_RX8010_SCL_PORT RTC_SCL_GPIO_Port +#define RTC_RX8010_SCL_PIN RTC_SCL_Pin + +static i2c_t *rtc; +static TIM_TypeDef *_timer_us; + +static void _delay_us(uint32_t us) +{ + if (_timer_us != NULL) + { + delay_hardware_us(_timer_us, us); + } + else + { + delay_us(us); + } +} + +/* sec, min, hour, week, day, month, year */ +// static uint8_t calendar[7] = {0, 0, 0, 1, 29, 2, 98}; + +/** + * @brief 从RTC芯片的指定地址读取一个字节数据 + * @param {uint8_t} *read_buf + * @param {uint8_t} addr + * @return {*} + */ +static BOOL rtc_read_byte(uint8_t *read_buf, uint8_t addr) +{ + uint8_t *p = read_buf; + + /* 发送起始信号 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送读地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 重新发送起始信号。前面的代码的目的向RTC传送地址,下面开始读取数据 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + /* 读取数据 */ + *p = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */ + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 从RTC芯片的指定地址读取若干数据 + * @param {uint8_t} *read_buf + * @param {uint8_t} addr + * @param {int} size + * @return {*} + */ +static BOOL rtc_read_bytes(uint8_t *read_buf, uint8_t addr, int size) +{ + int i = 0; + + /* 发送起始信号 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送读地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 重新发送起始信号。前面的代码的目的向RTC传送地址,下面开始读取数据 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 循环读取数据,RTC芯片地址自动自增 */ + for (i = 0; i < size; i++) + { + /* 每读完1个字节后,需要发送Ack, 最后一个字节需要发Nack */ + if (i != (size - 1)) + { + read_buf[i] = rtc->interface.read_byte(rtc, TRUE); /* 读1个字节 */ + } + else + { + read_buf[i] = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */ + } + } + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 向RTC芯片的指定地址写入一个数据 + * @param {uint8_t} data + * @param {uint8_t} addr + * @return {*} + * @note + */ +static BOOL rtc_write_byte(uint8_t data, uint8_t addr) +{ + int retry = 0; + + /* 尝试与RTC芯片建立I2C通讯 */ + for (retry = 0; retry < 100; retry++) + { + rtc->interface.start(rtc); /* 发送起始信号 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */ + if (rtc->interface.wait_ack(rtc) == TRUE) + { + break; + } + } + if (retry == 100) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送起始写地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 写入数据 */ + rtc->interface.write_byte(rtc, data); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 向RTC芯片的指定地址写入若干数据 + * @param {uint8_t} *write_buf + * @param {uint8_t} addr + * @param {int} size + * @return {*} + * @note + */ +static BOOL rtc_write_bytes(uint8_t *write_buf, uint8_t addr, int size) +{ + int i = 0; + int retry = 0; + + for (i = 0; i < size; i++) + { + if (i == 0) + { + /* 尝试与RTC芯片建立I2C通讯 */ + for (retry = 0; retry < 100; retry++) + { + rtc->interface.start(rtc); /* 发送起始信号 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */ + if (rtc->interface.wait_ack(rtc) == TRUE) + { + break; + } + } + if (retry == 100) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送起始写地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + } + + /* 循环写入数据,RTC芯片地址自动自增 */ + rtc->interface.write_byte(rtc, write_buf[i]); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + } + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 假读RTC芯片,任意读地址,不判断RTC响应 + * @return {*} + * @note + */ +static void rtc_dummy_read(void) +{ + rtc->interface.start(rtc); + rtc->interface.write_byte(rtc, RTC_WR_ADDR); + rtc->interface.write_byte(rtc, 0x20); + + rtc->interface.start(rtc); + rtc->interface.write_byte(rtc, RTC_RD_ADDR); + rtc->interface.read_byte(rtc, FALSE); + + rtc->interface.stop(rtc); +} + +/** + * @brief 用于检查VLF,寄存器地址:0x1e bit[1] + * @return {uint8_t} 0 = VLF位为0,1 = VLF位为1 + * @note + */ +static uint8_t rtc_check_vlf(void) +{ + uint8_t flag_register = 1; + uint8_t vlf = 0; + + rtc_read_byte(&flag_register, RTC_FLAG_ADDR); + + vlf = (flag_register & 0x02); + if (vlf == 0) + { + return 0; + } + else + { + return 1; + } +} + +/** + * @brief 等待VLF位清除,寄存器地址:0x1e bit[1] + * @return {uint8_t} 0 = 清零成功,1 = 清零失败,2 = 无需清零 + */ +static uint8_t rtc_wait_vlf_clear(void) +{ + uint8_t ret = 1; + uint8_t i = 0; + uint8_t vlf; + + for (i = 0; i < 10; i++) + { + vlf = rtc_check_vlf(); + + if (vlf == 0) + { + ret = ((i > 0) ? 0 : 2); + return ret; + } + + /* 清除VLF */ + rtc_write_byte(0, RTC_FLAG_ADDR); + } + + return ret; +} + +/** + * @brief 复位 + * @return {BOOL} FALSE = 复位成功,TRUE = 复位失败 + */ +static BOOL rtc_soft_reset(void) +{ + BOOL ret = FALSE; + + ret = rtc_write_byte(0x00, 0x1f); + ret = rtc_write_byte(0x80, 0x1f); + ret = rtc_write_byte(0xd3, 0x60); + ret = rtc_write_byte(0x03, 0x66); + ret = rtc_write_byte(0x02, 0x6b); + ret = rtc_write_byte(0x01, 0x6b); + + if (ret == 0) + { + _delay_us(2000); + } + + return ret; +} + +/** + * @brief 时钟寄存器初始化函数 + * @return {BOOL} TRUE = 初始化成功,FALSE = 初始化失败 + */ +static BOOL rtc_clock_reginit(void) +{ + BOOL ret = FALSE; + + /* set reserve register */ + ret = rtc_write_byte(RTC_REG17_DATA, RTC_REG17_ADDR); + ret = rtc_write_byte(RTC_REG30_DATA, RTC_REG30_ADDR); + ret = rtc_write_byte(RTC_REG31_DATA, RTC_REG31_ADDR); + ret = rtc_write_byte(RTC_IRQ_DATA, RTC_IRQ_ADDR); + /* write 0x04 to reg_0x1d */ + ret = rtc_write_byte(0x04, 0x1d); + /* write 0x00 to reg_0x1e */ + ret = rtc_write_byte(0x00, 0x1e); + /* stop clock */ + ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR); + /* set the present time */ + // ret = rtc_write_bytes(calendar, RTC_CLOCK_ADDR, sizeof(calendar)); + /* start clock */ + ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR); + + return ret; +} + +/** + * @brief RTC芯片初始化 + * @return {BOOL} TRUE = 成功,FALSE = 失败 + * @note + */ +BOOL rtc_init(TIM_TypeDef *timer_us) +{ + if (timer_us != NULL) + { + _timer_us = timer_us; + ENABLE_TIM_COUNT(_timer_us); + } + + i2c_gpio_group_t gpios; + int ret = 1; + gpios.scl = gpio_create(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN); + gpios.sda = gpio_create(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN); + + rtc = i2c_create(gpios, 10); + + rtc_dummy_read(); + /* wait for VLF bit clear */ + ret = rtc_wait_vlf_clear(); + if (ret == 0) + { + /* software reset */ + ret = rtc_soft_reset(); + if (ret == FALSE) + { + return FALSE; + } + } + else if (ret == 1) + { + return FALSE; + } + + /* register initialize */ + + return rtc_clock_reginit(); +} + +/** + * @brief RTC芯片反初始化 + * @return {*} + * @note + */ +BOOL rtc_dinit(void) +{ + GPIO_SET_ANALOG(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN); + GPIO_SET_ANALOG(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN); + return TRUE; +} + +/** + * @brief 从RTC芯片读取时间 + * @param {uint8_t} *read_buf - 接收缓存指针,用于接收读取到的数据 + * @return {BOOL} TRUE = 成功,FALSE = 失败 + * @note + */ +BOOL rtc_get_clock_time(uint8_t *read_buf) +{ + return rtc_read_bytes(read_buf, RTC_CLOCK_ADDR, 7); +} + +/** + * @brief 向RTC芯片写入时间 + * @param {rtc_date} *data - 发送缓存指针,用于存储要发送的数据 + * @return {BOOL} TRUE = 成功,FALSE = 失败 + * @note + */ +BOOL rtc_set_clock_time(rtc_date *data) +{ + BOOL ret = FALSE; + uint8_t tmp[7]; + tmp[0] = data->second; + tmp[1] = data->minute; + tmp[2] = data->hour; + tmp[3] = data->weekday; + tmp[4] = data->day; + tmp[5] = data->month; + tmp[6] = data->year; + + tmp[3] = (rtc_week_e)tmp[3]; // 改成星期几 + + /* stop clock */ + ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR); + /* set the present time */ + ret = rtc_write_bytes(tmp, RTC_CLOCK_ADDR, sizeof(tmp)); + /* start clock */ + ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR); + + return ret; +} + +/** + * 获取实时时钟(RTC)的时间戳 + * + * 该函数从RTC设备中获取当前的日期和时间,并将其转换为自1970年1月1日以来的秒数(时间戳)。 + * 如果无法从RTC设备中获取时间,则返回0。 + * + * @return 返回从1970年1月1日以来的秒数(时间戳),如果无法获取时间则返回0。 + */ +uint32_t rtc_timestamp(void) +{ + BOOL ret = FALSE; + uint8_t tmp[7]; + rtc_date_t date; + rtc_time_t time; + ret = rtc_get_clock_time(tmp); + if (ret == FALSE) + { + return 0; + } + date.year = hex_format_dec(tmp[6]); + date.month = hex_format_dec(tmp[5]); + date.day = hex_format_dec(tmp[4]); + time.hour = hex_format_dec(tmp[2]); + time.minute = hex_format_dec(tmp[1]); + time.second = hex_format_dec(tmp[0]); + return time2stamp(&date, &time); +} + +/** + * @brief 将星期转为星期码 + * @param {uint8_t} *weekday 星期几 + * @return {*} + * @note + */ +void rtc_weekday_convert(uint8_t *weekday) +{ + switch (*weekday) + { + case 1: + *weekday = MON; + break; + case 2: + *weekday = TUE; + break; + case 3: + *weekday = WED; + break; + case 4: + *weekday = THUR; + break; + case 5: + *weekday = FRI; + break; + case 6: + *weekday = SAT; + break; + case 7: + *weekday = SUN; + break; + default: + *weekday = 0; + break; + } +} + +/** + * @brief 将星期码转为星期 + * @param {uint8_t} *weekday 星期码 + * @return {*} + * @note + */ +void rtc_weekday_rconvert(uint8_t *weekday) +{ + switch (*weekday) + { + case MON: + *weekday = 1; + break; + case TUE: + *weekday = 2; + break; + case WED: + *weekday = 3; + break; + case THUR: + *weekday = 4; + break; + case FRI: + *weekday = 5; + break; + case SAT: + *weekday = 6; + break; + case SUN: + *weekday = 7; + break; + default: + *weekday = 0; + break; + } +} diff --git a/rtc_rx8010.h b/rtc_rx8010.h new file mode 100644 index 0000000..7a54745 --- /dev/null +++ b/rtc_rx8010.h @@ -0,0 +1,99 @@ +/** + * @file rtc_rx8010.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief 头文件 rtc_rx8010.h + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __RTC_RX8010_H__ +#define __RTC_RX8010_H__ +#include "main.h" + +#define RTC_DEVICE_ADDR 0x32 + +#define RTC_WR_ADDR ((RTC_DEVICE_ADDR << 1) | 0) +#define RTC_RD_ADDR ((RTC_DEVICE_ADDR << 1) | 1) + +#define RTC_FLAG_ADDR 0x1e +#define RTC_CLOCK_ADDR 0x10 +#define RTC_CONTROL_ADDR 0x1f + +#define RTC_REG17_ADDR 0x17 +#define RTC_REG17_DATA 0xd8 + +#define RTC_REG30_ADDR 0x30 +#define RTC_REG30_DATA 0x00 + +#define RTC_REG31_ADDR 0x31 +#define RTC_REG31_DATA 0x08 + +#define RTC_IRQ_ADDR 0x32 +#define RTC_IRQ_DATA 0x00 + +typedef enum +{ + SUN = BIT0, + MON = BIT1, + TUE = BIT2, + WED = BIT3, + THUR = BIT4, + FRI = BIT5, + SAT = BIT6 +} rtc_week_e; ///< 星期码 + +typedef struct +{ + uint8_t year; ///< 7 bit - 1 63 + uint8_t month; ///< 4 bit + uint8_t day; ///< 5 bit + uint8_t weekday; ///< rtc_week_e + uint8_t hour; ///< 5 bit + uint8_t minute; ///< 6 bit + uint8_t second; ///< 6 bit +} rtc_date; + +/** + * @brief Initializes the RTC module. + * @return TRUE if the initialization is successful, FALSE otherwise. + */ +extern BOOL rtc_init(TIM_TypeDef *timer_us); + +/** + * @brief Deinitializes the RTC module. + * @return TRUE if the deinitialization is successful, FALSE otherwise. + */ +extern BOOL rtc_dinit(void); + +/** + * @brief Retrieves the current clock time from the RTC module. + * @param read_buf Pointer to the buffer to store the clock time. + * @return TRUE if the clock time is successfully retrieved, FALSE otherwise. + */ +extern BOOL rtc_get_clock_time(uint8_t *read_buf); + +/** + * @brief Sets the clock time in the RTC module. + * @param data Pointer to the RTC date structure containing the new clock time. + * @return TRUE if the clock time is successfully set, FALSE otherwise. + */ +extern BOOL rtc_set_clock_time(rtc_date *data); + +/** + * @brief Retrieves the current alarm time from the RTC module. + * @param read_buf Pointer to the buffer to store the alarm time. + * @return TRUE if the alarm time is successfully retrieved, FALSE otherwise. + */ +extern uint32_t rtc_timestamp(void); +/** + * @brief Converts the weekday value to a human-readable format. + * @param weekday Pointer to the weekday value to be converted. + */ +extern void rtc_weekday_convert(uint8_t *weekday); + +/** + * @brief Converts the weekday value from a human-readable format to the RTC format. + * @param weekday Pointer to the weekday value to be converted. + */ +extern void rtc_weekday_rconvert(uint8_t *weekday); +#endif ///< !__RTC_RX8010_H__ diff --git a/sht40.c b/sht40.c new file mode 100644 index 0000000..6235ef8 --- /dev/null +++ b/sht40.c @@ -0,0 +1,181 @@ +#include "sht40.h" +#include "i2cs.h" +#include "delay.h" +#define SHT40_SDA_PORT I2C1_SDA_GPIO_Port +#define SHT40_SDA_PIN I2C1_SDA_Pin +#define SHT40_SCL_PORT I2C1_SCL_GPIO_Port +#define SHT40_SCL_PIN I2C1_SCL_Pin + +#define SHT40_I2C_ADDRESS 0x44 +#define SHT40_MEASURE_CMD 0xFD // 2*8-bit T-data:8-bit CRC:2*8-bit RH-data: 8-bit CRC + +static i2c_t *_sht40_i2c; +static TIM_TypeDef *_timer_us; +static uint8_t crc8(uint8_t *data, uint8_t len); + +static void _delay_us(uint32_t us) +{ + if (_timer_us != NULL) + { + delay_hardware_us(_timer_us, us); + } + else + { + delay_us(us); + } +} + +void sht40_test(void) +{ + float32 temperature = 0; + float32 humidity = 0; + sht40_read(&temperature, &humidity); + + __NOP(); +} +/** + * @brief 初始化 SHT40 传感器 + * + * 该函数用于初始化 SHT40 传感器,包括创建 I2C 通讯所需的 GPIO 引脚和 I2C 通讯对象。 + * + * 初始化完成后,sht40_i2c 变量将用于后续的 SHT40 传感器通信。 + */ +void sht40_init(TIM_TypeDef *timer_us) +{ + DBG_ASSERT(timer_us != NULL __DBG_LINE); + if (timer_us != NULL) + { + _timer_us = timer_us; + ENABLE_TIM_COUNT(_timer_us); + } + + i2c_gpio_group_t gpios; + gpios.scl = gpio_create(SHT40_SCL_PORT, SHT40_SCL_PIN); + gpios.sda = gpio_create(SHT40_SDA_PORT, SHT40_SDA_PIN); + + _sht40_i2c = i2c_create(gpios, 10); + DBG_ASSERT(_sht40_i2c != NULL __DBG_LINE); + + // sht40_test(); // 测试 SHT40 +} + +/** + * @brief 初始化 SHT40 传感器 + * + * 此函数用于初始化 SHT40 温湿度传感器,以便进行后续的温度和湿度测量。 + * + * 注意:该函数没有返回值,也不接受任何参数。 + */ +void sht40_dinit(void) +{ + GPIO_SET_ANALOG(SHT40_SDA_PORT, SHT40_SDA_PIN); + GPIO_SET_ANALOG(SHT40_SCL_PORT, SHT40_SCL_PIN); +} + +/** + * @brief 读取温湿度传感器数据 + * + * 从温湿度传感器读取温度和湿度数据,并将读取到的数据存储在传入的浮点数指针指向的位置。 + * + * @param temperature 用于存储读取到的温度值的浮点数指针 + * @param humidity 用于存储读取到的湿度值的浮点数指针 + */ +BOOL sht40_read(float32 *temperature, float32 *humidity) +{ + uint8_t data[6]; + + osel_memset(data, 0, ARRAY_LEN(data)); + // 发送开始信号 + _sht40_i2c->interface.start(_sht40_i2c); + // 发送写入地址命令 + _sht40_i2c->interface.write_byte(_sht40_i2c, SHT40_I2C_ADDRESS << 1); + // 等待写入地址命令响应 + if (_sht40_i2c->interface.wait_ack(_sht40_i2c) == FALSE) + { + return FALSE; + } + // 发送测量命令 + _sht40_i2c->interface.write_byte(_sht40_i2c, SHT40_MEASURE_CMD); + // 等待测量命令响应 + if (_sht40_i2c->interface.wait_ack(_sht40_i2c) == FALSE) + { + return FALSE; + } + // 停止I2C总线 + _sht40_i2c->interface.stop(_sht40_i2c); + + _delay_us(10000); // 根据 SHT40 数据手册,等待至少 10ms + + // 发送开始信号 + _sht40_i2c->interface.start(_sht40_i2c); + // 发送写入地址命令 + _sht40_i2c->interface.write_byte(_sht40_i2c, (SHT40_I2C_ADDRESS << 1) | 1); + // 等待写入地址命令响应 + if (_sht40_i2c->interface.wait_ack(_sht40_i2c) == FALSE) + { + return FALSE; + } + + for (uint8_t i = 0; i < ARRAY_LEN(data); i++) + { + if (i == 5) + { + data[i] = _sht40_i2c->interface.read_byte(_sht40_i2c, FALSE); + } + else + { + data[i] = _sht40_i2c->interface.read_byte(_sht40_i2c, TRUE); + } + } + + // 停止I2C总线 + _sht40_i2c->interface.stop(_sht40_i2c); + + *temperature = 0; + *humidity = 0; + if (crc8(&data[0], 2) != data[2] || crc8(&data[3], 2) != data[5]) + { + return FALSE; + } + else + { + *temperature = (float32)((uint16_t)data[0] << 8 | data[1]) * 175 / 65535 - 45; + *humidity = (float32)((uint16_t)data[3] << 8 | data[4]) * 125 / 65535 - 6; + if (*humidity > 100) + { + *humidity = 100; + } + if (*humidity < 0) + { + *humidity = 0; + } + + return TRUE; + } +} + +/** + * @brief crc8校验函数,多项式为 x^8 + x^5 + x^4 + 1 + * @param data 要校验的数据 + * @param len 要校验的数据的字节数 + * @retval 校验结果 + * @note 该校验适合SHT3温湿度传感器的数据校验 + */ +static uint8_t crc8(uint8_t *data, uint8_t len) +{ + const uint8_t polynomial = 0x31; + uint8_t crc = 0xFF; + int i, j; + + for (i = 0; i < len; ++i) + { + crc ^= *data++; + + for (j = 0; j < 8; ++j) + { + crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1); + } + } + + return crc; +} diff --git a/sht40.h b/sht40.h new file mode 100644 index 0000000..c06341c --- /dev/null +++ b/sht40.h @@ -0,0 +1,9 @@ +#ifndef __SHT40_H +#define __SHT40_H +#include "main.h" + +void sht40_init(TIM_TypeDef *timer_us); ///< 初始化 SHT40 传感器 +void sht40_dinit(void); ///< 反初始化 SHT40 传感器 +BOOL sht40_read(float32 *temperature, float32 *humidity); ///< 读取温湿度传感器数据 + +#endif ///< !__SHT40_H diff --git a/ssd1306_oled.c b/ssd1306_oled.c new file mode 100644 index 0000000..a232cae --- /dev/null +++ b/ssd1306_oled.c @@ -0,0 +1,1011 @@ +/** + * @file ssd1306_oled.c + * @author xushenghao + * @brief 0.96 SSD1306 OLED display driver + * @version 0.1 + * @note PB13-SCK PB12-SDA + */ +#include "ssd1306_oled.h" +#include "string.h" + +/************************************6*8的点阵************************************/ +const uint8_t F6x8[][6] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp + 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! + 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " + 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # + 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ + 0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // % + 0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // & + 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // ' + 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // ( + 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // ) + 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // * + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + + 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // , + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - + 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / + 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 + 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 + 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 + 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 + 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 + 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 + 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 + 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 + 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 + 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 + 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // : + 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ; + 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // < + 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // = + 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // > + 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ? + 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @ + 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C + 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F + 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G + 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H + 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I + 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J + 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K + 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L + 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M + 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P + 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q + 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R + 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S + 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T + 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U + 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V + 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W + 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X + 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y + 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ + 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55 + 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ] + 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^ + 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _ + 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // ' + 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a + 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b + 0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c + 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d + 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e + 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f + 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g + 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h + 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i + 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j + 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k + 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l + 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n + 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o + 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p + 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r + 0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s + 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t + 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u + 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v + 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w + 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x + 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y + 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // horiz lines +}; +/****************************************8*16的点阵************************************/ +const uint8_t F8X16[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 + 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1 + 0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2 + 0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3 + 0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4 + 0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5 + 0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6 + 0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7 + 0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8 + 0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9 + 0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10 + 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15 + 0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16 + 0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17 + 0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18 + 0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19 + 0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20 + 0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21 + 0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22 + 0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23 + 0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24 + 0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25 + 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26 + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27 + 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28 + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29 + 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30 + 0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31 + 0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32 + 0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33 + 0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34 + 0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35 + 0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36 + 0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37 + 0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38 + 0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40 + 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41 + 0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42 + 0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44 + 0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45 + 0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46 + 0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47 + 0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48 + 0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49 + 0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50 + 0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51 + 0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53 + 0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54 + 0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55 + 0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56 + 0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57 + 0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58 + 0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59 + 0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60 + 0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61 + 0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63 + 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65 + 0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69 + 0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71 + 0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72 + 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73 + 0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74 + 0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75 + 0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77 + 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79 + 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81 + 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83 + 0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84 + 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85 + 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86 + 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87 + 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88 + 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89 + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92 + 0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93 + 0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94 +}; + +const uint8_t LOGO[] = { + 0x00, 0x03, 0x05, 0x09, 0x11, 0xFF, 0x11, 0x89, 0x05, 0xC3, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0xFF, 0x11, 0xAA, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x01, 0x38, 0x44, 0x82, 0x92, + 0x92, 0x74, 0x01, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x44, 0xC7, 0x01, 0x7D, + 0x7D, 0x7D, 0x7D, 0x01, 0x7D, 0x7D, 0x7D, 0x7D, 0x01, 0x7D, 0x7D, 0x7D, 0x7D, 0x01, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0x00, 0x00, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0xE6, 0x66, 0x20, 0x00, 0x06, 0x06, 0x86, 0x06, + 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x86, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, + 0x00, 0x86, 0x86, 0x86, 0x86, 0x86, 0x80, 0x80, 0x86, 0x86, 0x06, 0x86, 0x86, 0xC0, 0xC0, 0x86, + 0x86, 0x86, 0x06, 0x06, 0xD0, 0x30, 0x76, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1C, 0x00, 0xFE, 0x00, 0x01, + 0x02, 0x00, 0xC4, 0x18, 0x20, 0x02, 0x9E, 0x63, 0xB2, 0x0E, 0x00, 0xFF, 0x81, 0x81, 0xFF, 0x00, + 0x00, 0x80, 0x40, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x23, 0xEA, 0xAA, 0xBF, 0xAA, + 0xEA, 0x03, 0x3F, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0C, 0x08, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81, 0x80, 0x80, 0x81, 0x80, + 0x81, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x09, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x1E, 0x21, 0x40, 0x40, 0x50, 0x21, 0x5E, 0x00, 0x1E, 0x21, 0x40, 0x40, 0x50, 0x21, 0x5E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC1, 0xC1, 0xFF, + 0xFF, 0xC1, 0xC1, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0xFC, 0xF3, 0xEF, 0xF3, 0xFC, + 0x80, 0xFF, 0x80, 0xEE, 0xEE, 0xEE, 0xF5, 0xFB, 0xFF, 0x9C, 0xBE, 0xB6, 0xB6, 0x88, 0xFF, 0x00, + // end +}; + +static uint8_t _buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; // 定义屏幕缓冲区 +static uint8_t _buffer_copy[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; // 屏幕缓冲区副本 + +static void i2c_start(void) +{ + SDA_OUT(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(4); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +static void i2c_stop(void) +{ + SDA_OUT(); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); +} + +static BOOL i2c_wait_ack(void) +{ + uint8_t count = 0; + SDA_IN(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(4); + while (GPIO_READ(SSD1306_SDA_PORT, SSD1306_SDA_PIN)) + { + count++; + if (count > 250) + { + i2c_stop(); + return FALSE; + } + } + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + return TRUE; +} + +static void i2c_ack(void) +{ + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + SDA_OUT(); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +static void i2c_nack(void) +{ + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + SDA_OUT(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +uint8_t i2c_read_byte(BOOL ack) +{ + uint8_t i = 0, receive = 0; + SDA_IN(); + for (i = 0; i < 8; i++) + { + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + receive <<= 1; + if (GPIO_READ(SSD1306_SDA_PORT, SSD1306_SDA_PIN)) + { + receive++; + } + delay_us(1); + } + + if (!ack) + { + i2c_nack(); + } + else + { + i2c_ack(); + } + + return receive; +} + +void i2c_write_byte(uint8_t data) +{ + uint8_t i = 0; + SDA_OUT(); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + for (i = 0; i < 8; i++) + { + // IIC_SDA=(txd&0x80)>>7; + if ((data & 0x80) >> 7) + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + else + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + + data <<= 1; + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + } +} + +static void i2c_write_command(uint8_t command) +{ + uint8_t dd[2]; + dd[0] = SSD1306_CMD_SET_LOW_COLUMN; // Co = 0, D/C# = 0 + dd[1] = command; + i2c_start(); + i2c_write_byte(SSD1306_I2C_ADDRESS); + i2c_wait_ack(); + + i2c_write_byte(dd[0]); + i2c_wait_ack(); + + i2c_write_byte(dd[1]); + i2c_wait_ack(); + i2c_stop(); +} + +static void i2c_write_data(uint8_t data) +{ + uint8_t dd[2]; + dd[0] = SSD1306_CMD_SET_START_LINE; // Co = 0, D/C# = 1 + dd[1] = data; + i2c_start(); + i2c_write_byte(SSD1306_I2C_ADDRESS); + i2c_wait_ack(); + + i2c_write_byte(dd[0]); + i2c_wait_ack(); + + i2c_write_byte(dd[1]); + i2c_wait_ack(); +} + +/** + * @brief 设置SSD1306 OLED显示屏上的显示位置 + * + * 该函数用于设置SSD1306 OLED显示屏上的显示位置,通过x和y坐标确定显示位置。 + * + * @param x 要设置的横坐标(0-127) + * @param y 要设置的纵坐标(0-7,对应SSD1306 OLED的8个页面) + */ +void set_position(uint8_t x, uint8_t y) +{ + i2c_write_command(0xb0 + y); + i2c_write_command(((x & 0xf0) >> 4) | 0x10); + i2c_write_command((x & 0x0f) | 0x01); +} + +void ssd1306_test(void) +{ + ssd1306_f8x16_string(0, 0, " TEST"); + ssd1306_f8x16_number(40, 0, -15.26, 2); + ssd1306_f6x8_string_number(0, 3, " @ADC1:", 'V', 24); + ssd1306_update_screen(); +} + +void ssd1306_init(void) +{ + i2c_write_command(SSD1306_CMD_DISPLAY_OFF); // display off + i2c_write_command(SSD1306_CMD_MEMORY_MODE); // Set Memory Addressing Mode + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid + i2c_write_command(SSD1306_CMD_COM_SCAN_DEC); // Set COM Output Scan Direction + i2c_write_command(SSD1306_CMD_SET_LOW_COLUMN); //---set low column address + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); //---set high column address + i2c_write_command(SSD1306_CMD_SET_START_LINE); //--set start line address + i2c_write_command(SSD1306_CMD_SET_CONTRAST); //--set contrast control register + i2c_write_command(0xff); // 亮度调节 0x00~0xff + i2c_write_command(0xa1); //--set segment re-map 0 to 127 + i2c_write_command(SSD1306_CMD_NORMAL_DISPLAY); //--set normal display + i2c_write_command(SSD1306_CMD_SET_MULTIPLEX); //--set multiplex ratio(1 to 64) + i2c_write_command(0x3f); // + i2c_write_command(SSD1306_CMD_DISPLAY_ALL_ON_RESUME); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content + i2c_write_command(SSD1306_CMD_SET_DISPLAY_OFFSET); //-set display offset + i2c_write_command(SSD1306_CMD_SET_LOW_COLUMN); //-not offset + i2c_write_command(SSD1306_CMD_SET_DISPLAY_CLOCK_DIV); //--set display clock divide ratio/oscillator frequency + i2c_write_command(0xf0); //--set divide ratio + i2c_write_command(SSD1306_CMD_SET_PRECHARGE); //--set pre-charge period + i2c_write_command(SSD1306_CMD_PAGE_ADDR); // + i2c_write_command(SSD1306_CMD_SET_COM_PINS); //--set com pins hardware configuration + i2c_write_command(0x12); + i2c_write_command(SSD1306_CMD_SET_VCOM_DETECT); //--set vcomh + i2c_write_command(SSD1306_CMD_MEMORY_MODE); // 0x20,0.77xVcc + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); //--set DC-DC enable + i2c_write_command(SSD1306_CMD_SET_DC_DC_ENABLE); // + i2c_write_command(SSD1306_CMD_DISPLAY_ON); //--turn on oled panel + ssd1306_clear(); + + // ssd1306_test(); +} + +void ssd1306_logo(void) +{ + ssd1306_draw_bmp(0, 0, SSD1306_WIDTH, 2, LOGO); +} + +void ssd1306_display_on(void) +{ + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); // 设置电荷泵 + i2c_write_command(SSD1306_CMD_SET_DC_DC_ENABLE); // 开启电荷泵 + i2c_write_command(SSD1306_CMD_DISPLAY_ON); // OLED唤醒 +} + +void ssd1306_display_off(void) +{ + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); // 设置电荷泵 + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); // 关闭电荷泵 + i2c_write_command(SSD1306_CMD_DISPLAY_OFF); // OLED休眠 +} + +/** + * @brief 更新SSD1306 OLED显示屏的内容 + * + * 此函数将缓冲区中的数据写入SSD1306 OLED显示屏,从而更新显示内容。 + * + * 首先,通过发送命令设置列地址和页地址,然后将缓冲区中的数据逐行写入显示屏。 + * + * @note 在调用此函数之前,需要将需要显示的数据写入缓冲区。 + */ +void ssd1306_update_screen(void) +{ + for (uint8_t i = 0; i < SSD1306_HEIGHT / 8; i++) + { + uint8_t update_needed = 0; + for (uint8_t j = 0; j < SSD1306_WIDTH; j++) + { + if (_buffer[j + i * SSD1306_WIDTH] != _buffer_copy[j + i * SSD1306_WIDTH]) + { + update_needed = 1; + break; + } + } + if (update_needed) + { + i2c_write_command(0xb0 + i); + i2c_write_command(0x01); + i2c_write_command(0x10); + for (uint8_t j = 0; j < SSD1306_WIDTH; j++) + { + i2c_write_data(_buffer[j + i * SSD1306_WIDTH]); + } + } + } + + osel_memcpy(_buffer_copy, _buffer, ARRAY_LEN(_buffer)); +} + +/** + * @brief 填充整个屏幕为指定颜色 + * + * 该函数将 SSD1306 OLED 显示屏的每一个像素点都设置为指定的颜色。 + * + * @param color 颜色值,SSD1306_BLACK 表示关闭像素点(黑色),SSD1306_WHITE 表示打开像素点(白色) + */ +void ssd1306_fill(uint8_t color) +{ + ssd1306_clear_buffer(); + osel_memset(_buffer, color, ARRAY_LEN(_buffer)); + ssd1306_update_screen(); +} + +/** + * @brief 清空SSD1306显示屏 + * + * 该函数通过向SSD1306显示屏发送一系列命令来清空显示内容。 + * + * 首先,通过循环遍历每个页面(SSD1306显示屏有8个页面), + * 对每个页面执行以下操作: + * 1. 发送页面地址命令(0xb0 + y),其中y为当前页面索引。 + * 2. 发送列地址低位命令(0x01)。 + * 3. 发送列地址高位命令(0x10),表示从第一列开始。 + * 4. 循环遍历每一列(SSD1306显示屏的宽度), + * 发送数据0x00以清空该列的内容。 + */ +void ssd1306_clear(void) +{ + ssd1306_clear_buffer(); + ssd1306_update_screen(); +} + +void ssd1306_clear_buffer(void) +{ + osel_memset(_buffer, SSD1306_BLACK, ARRAY_LEN(_buffer)); + osel_memset(_buffer_copy, 0xff, ARRAY_LEN(_buffer_copy)); +} + +/** + * @brief 在指定区域内绘制BMP + * + * 在指定坐标区域内绘制一个BMP,使用SSD1306 OLED显示屏 + * ssd1306_draw_bmp(0, 0, SSD1306_WIDTH, 2); + * @param x0 BMP绘制的起始X坐标 + * @param y0 BMP绘制的起始Y坐标 + * @param x1 BMP绘制的结束X坐标 + * @param y1 BMP绘制的结束Y坐标 + */ +void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp) +{ + uint8_t j = 0; + uint8_t x, y; + ssd1306_clear_buffer(); + if (y1 % 8 == 0) + y = y1 / 8; + else + y = y1 / 8 + 1; + for (y = y0; y < y1; y++) + { + set_position(x0, y); + for (x = x0; x < x1; x++) + { + i2c_write_data(bmp[j++]); + } + } +} + +/** + * @brief 在SSD1306 OLED显示屏上显示字符串 + * + * 在SSD1306 OLED显示屏上以6x8像素的字体显示给定的字符串。 + * + * @param x 显示字符串的起始x坐标 + * @param y 显示字符串的起始y坐标 + * @param str 要显示的字符串 + */ +void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch) +{ + uint8_t c = 0, i = 0, j = 0; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } +} + +/** + * @brief 将浮点数显示在SSD1306显示屏上 + * + * 该函数将给定的浮点数num转换为字符串,并将其显示在SSD1306显示屏的指定位置。 + * + * @param x 显示的起始x坐标 + * @param y 显示的起始y坐标 + * @param num 要显示的浮点数 + * @param dot_num 小数点后的位数,0表示不显示小数部分 + */ +void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num) +{ + uint8_t ch[9] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + uint8_t c = 0, i = 0, j = 0; + if (num < 0) + { + ch[i++] = '-'; + num = -num; + } + if (num > 32000) + return; + + c = 8 - i; + + if (num >= 10000) + { + ch[i++] = num / 10000 + 48; + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 1000) + { + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 100) + { + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 10) + { + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 0) + { + ch[i++] = (int32_t)(num) % 10 + 48; + } + if (dot_num > 0 && i < 7) + { + ch[i++] = '.'; + num = num - (int32_t)num; + + if (dot_num == 1 && i < 8) + { + num = num * 10; + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + if (dot_num >= 2 && i < 8) + { + num = num * 100; + ch[i++] = (int32_t)num % 100 / 10 + 48; + if (i < 8) + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + } + + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } +} +/** + * @brief 在SSD1306 OLED屏幕上显示字符串和数字 + * + * 该函数用于在SSD1306 OLED屏幕上显示字符串和数字。首先显示一个字符串, + * 然后显示一个浮点数。字符串和数字按照指定的位置和单位显示。 + * + * @param x 起始位置的x坐标 + * @param y 起始位置的y坐标 + * @param ch 要显示的字符串 + * @param unit 数字的单位,例如'm'表示米 + * @param num 要显示的浮点数 + */ +void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num) +{ + uint8_t c = 0, i = 0, j = 0; + int8_t a, number[7] = {0, 0, 0, -2, 0, 0, 0}; + uint32_t d; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } + d = 1000 * num; + for (a = 0; a < 3; a++) + { + number[6 - a] = d % 10; + d = d / 10; + } + for (a = 4; a < 7; a++) + { + number[6 - a] = d % 10; + d = d / 10; + } + if (num >= 100) + { + a = 0; + } + else if (num >= 10) + { + a = 1; + } + else if (num >= 0) + { + a = 2; + } + for (; a < 7; a++) + { + c = number[a] + 16; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } + for (int h = 0; h < 7; h++) + { + c = unit - 32; + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + } +} + +/** + * @brief 在SSD1306 OLED显示屏上显示8x16大小的字符串 + * + * 该函数使用8x16字体在SSD1306 OLED显示屏上显示指定的字符串。字符串的字符位置由x和y参数指定。 + * + * @param x 显示字符串的起始x坐标 + * @param y 显示字符串的起始y坐标 + * @param str 要显示的字符串 + */ +void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch) +{ + uint8_t c = 0, i = 0, j = 0; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 8; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i]; + for (i = 0; i < 8; i++) + _buffer[((y + 1) * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i + 8]; + x += 8; + j++; + } +} + +/** + * @brief 在SSD1306 OLED显示屏上以8x16像素字体显示浮点数 + * + * 在指定的坐标位置 (x, y) 上,使用8x16像素大小的字体显示浮点数 num,并显示指定数量的小数点 dot_num。 + * + * @param x 显示位置的x坐标 + * @param y 显示位置的y坐标 + * @param num 要显示的浮点数 + * @param dot_num 要显示的小数点数量 + */ +void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num) +{ + uint8_t ch[9] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + uint8_t c = 0, i = 0, j = 0; + if (num < 0) + { + ch[i++] = '-'; + num = -num; + } + if (num > 32000) + return; + + c = 8 - i; + + if (num >= 10000) + { + ch[i++] = num / 10000 + 48; + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 1000) + { + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 100) + { + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 10) + { + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 0) + { + ch[i++] = (int32_t)(num) % 10 + 48; + } + if (dot_num > 0 && i < 7) + { + ch[i++] = '.'; + num = num - (int32_t)num; + + if (dot_num == 1 && i < 8) + { + num = num * 10; + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + if (dot_num >= 2 && i < 8) + { + num = num * 100; + ch[i++] = (int32_t)num % 100 / 10 + 48; + if (i < 8) + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + } + + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 8; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i]; + for (i = 0; i < 8; i++) + _buffer[((y + 1) * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i + 8]; + x += 8; + j++; + } +} + +// 在 SSD1306 显示屏上绘制一个像素 +void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color) +{ + if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) + { + // 超出屏幕范围 + return; + } + + // 设置像素颜色 + if (color == SSD1306_WHITE) + { + _buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8); + } + else + { + _buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8)); + } +} + +// 在 SSD1306 显示屏上绘制一条线 +void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) +{ + int16_t dx, dy, sx, sy, err, e2; + + dx = ABS(x2 - x1); + dy = ABS(y2 - y1); + sx = (x1 < x2) ? 1 : -1; + sy = (y1 < y2) ? 1 : -1; + err = dx - dy; + + while (1) + { + ssd1306_draw_pixel(x1, y1, color); + if (x1 == x2 && y1 == y2) + break; + e2 = err * 2; + if (e2 > -dy) + { + err -= dy; + x1 += sx; + } + if (e2 < dx) + { + err += dx; + y1 += sy; + } + } +} + +// 在 SSD1306 显示屏上绘制一个矩形 +void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + // 绘制矩形的四条边 + ssd1306_draw_line(x, y, x + w, y, color); // 上边 + ssd1306_draw_line(x, y + h, x + w, y + h, color); // 下边 + ssd1306_draw_line(x, y, x, y + h, color); // 左边 + ssd1306_draw_line(x + w, y, x + w, y + h, color); // 右边 +} + +// 在 SSD1306 显示屏上绘制一个填充矩形 +void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + for (uint8_t i = 0; i < h; i++) + { + ssd1306_draw_line(x, y + i, x + w, y + i, color); + } +} + +// 绘制进度条 +void ssd1306_draw_progress_bar(uint8_t progress) +{ + uint8_t offset_y = 10; + char progress_text[5]; // 用于存储百分比文本 + // 绘制边框 + ssd1306_draw_rect_angle(10, 24 + offset_y, 108, 10, SSD1306_WHITE); + + // 绘制进度条 + ssd1306_fill_rect_angle(12, 26 + offset_y, 4 + progress, 7, SSD1306_WHITE); + + // 显示百分比文本 + snprintf(progress_text, sizeof(progress_text), "%3d%%", progress); + ssd1306_f8x16_string(SSD1306_WIDTH * 0.35, 2, (const uint8_t *)progress_text); + + // 更新显示 + ssd1306_update_screen(); +} + +// 在中间显示文字 +void ssd1306_draw_text_center(uint8_t y, const char *text) +{ + uint8_t x = (SSD1306_WIDTH - strlen(text) * 6) / 2; + ssd1306_f6x8_string(x, y, (const uint8_t *)text); + + // 更新显示 + ssd1306_update_screen(); +} diff --git a/ssd1306_oled.h b/ssd1306_oled.h new file mode 100644 index 0000000..32a3304 --- /dev/null +++ b/ssd1306_oled.h @@ -0,0 +1,80 @@ +#ifndef __SSD1306_OLED_H +#define __SSD1306_OLED_H + +#include "main.h" + +// OLED引脚定义 +#define SSD1306_SDA_PORT OLED_SDA_GPIO_Port +#define SSD1306_SDA_PIN OLED_SDA_Pin +#define SSD1306_SCK_PORT OLDE_SCK_GPIO_Port +#define SSD1306_SCK_PIN OLDE_SCK_Pin + +// I2C地址 +#define SSD1306_I2C_ADDRESS 0x78 +// OLED显示参数 +#define SSD1306_WIDTH 128 +#define SSD1306_HEIGHT 64 +// OLED颜色 +#define SSD1306_WHITE 1 +#define SSD1306_BLACK 0 + +// OLED命令定义 +#define SSD1306_CMD_DISPLAY_OFF 0xAE +#define SSD1306_CMD_DISPLAY_ON 0xAF +#define SSD1306_CMD_SET_CONTRAST 0x81 +#define SSD1306_CMD_DISPLAY_ALL_ON_RESUME 0xA4 +#define SSD1306_CMD_DISPLAY_ALL_ON 0xA5 +#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 +#define SSD1306_CMD_INVERT_DISPLAY 0xA7 +#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3 +#define SSD1306_CMD_SET_COM_PINS 0xDA +#define SSD1306_CMD_SET_VCOM_DETECT 0xDB +#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIV 0xD5 +#define SSD1306_CMD_SET_PRECHARGE 0xD9 +#define SSD1306_CMD_SET_MULTIPLEX 0xA8 +#define SSD1306_CMD_SET_LOW_COLUMN 0x00 +#define SSD1306_CMD_SET_HIGH_COLUMN 0x10 +#define SSD1306_CMD_SET_START_LINE 0x40 +#define SSD1306_CMD_MEMORY_MODE 0x20 +#define SSD1306_CMD_COLUMN_ADDR 0x21 +#define SSD1306_CMD_PAGE_ADDR 0x22 +#define SSD1306_CMD_COM_SCAN_INC 0xC0 +#define SSD1306_CMD_COM_SCAN_DEC 0xC8 +#define SSD1306_CMD_SEG_REMAP 0xA0 +#define SSD1306_CMD_CHARGE_PUMP 0x8D +#define SSD1306_CMD_SET_DC_DC_ENABLE 0x14 + +#define SDA_OUT() \ + { \ + GPIO_SET_OUTPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \ + } + +#define SDA_IN() \ + { \ + GPIO_SET_INPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \ + } + +// 函数声明 +void ssd1306_init(void); +void ssd1306_logo(void); +void ssd1306_display_on(void); +void ssd1306_display_off(void); +void ssd1306_update_screen(void); + +void ssd1306_fill(uint8_t color); +void ssd1306_clear(void); +void ssd1306_clear_buffer(void); +void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp); +void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch); +void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num); +void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num); +void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch); +void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num); + +void ssd1306_draw_text_center(uint8_t y, const char *text); +void ssd1306_draw_progress_bar(uint8_t progress); +void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color); +void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color); +void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color); +void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color); +#endif // __SSD1306_OLED_H diff --git a/tca9555.c b/tca9555.c new file mode 100644 index 0000000..831acc3 --- /dev/null +++ b/tca9555.c @@ -0,0 +1,129 @@ +#include "tca9555.h" +#include "i2cs.h" +#define I2C_MASTER_SCL_PORT I2C1_SCL_GPIO_Port +#define I2C_MASTER_SCL_PIN I2C1_SCL_Pin +#define I2C_MASTER_SDA_PORT I2C1_SDA_GPIO_Port +#define I2C_MASTER_SDA_PIN I2C1_SDA_Pin +static i2c_t *tca9555_i2c; + +void tca9555_init(void) +{ + i2c_gpio_group_t gpios; + gpios.scl = gpio_create(I2C_MASTER_SCL_PORT, I2C_MASTER_SCL_PIN); + gpios.sda = gpio_create(I2C_MASTER_SDA_PORT, I2C_MASTER_SDA_PIN); + + tca9555_i2c = i2c_create(gpios, 0); + + tca9555_register reg; + osel_memset((uint8_t *)®, 0, sizeof(tca9555_register)); + tca9555_write_config(®); + + osel_memset((uint8_t *)®, 0, sizeof(tca9555_register)); + reg.port.p0.asint = 0; + reg.port.p1.asint = 0; + tca9555_write_output(®); +} + +void tca9555_configure_input(tca9555_register *reg, uint8_t port, uint8_t pin) +{ + if (port == 0) + { + reg->port.p0.asint |= (1 << pin); // 设置对应的引脚为输入 + } + else if (port == 1) + { + reg->port.p1.asint |= (1 << pin); // 设置对应的引脚为输入 + } + tca9555_write_config(reg); +} + +uint8_t tca9555_read_single_register(tca9555_reg_t address) +{ + uint8_t reg_data = 0; // 定义一个8位无符号字符型变量reg_data,用于存储读取到的寄存器值 + tca9555_i2c->interface.start(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, WRITE_ADDRESS); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, address); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + + tca9555_i2c->interface.start(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, READ_ADDRESS); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + reg_data = tca9555_i2c->interface.read_byte(tca9555_i2c, FALSE); + tca9555_i2c->interface.stop(tca9555_i2c); + return reg_data; +} + +void tca9555_write_single_register(tca9555_reg_t address, uint8_t reg_val) +{ + tca9555_i2c->interface.start(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, WRITE_ADDRESS); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, address); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, reg_val); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.stop(tca9555_i2c); +} + +void tca9555_read_struct(tca9555_register *reg, tca9555_reg_t reg_num) +{ + tca9555_i2c->interface.start(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, WRITE_ADDRESS); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + + tca9555_i2c->interface.start(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, READ_ADDRESS); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + + reg->port.p0.asint = tca9555_i2c->interface.read_byte(tca9555_i2c, TRUE); + reg->port.p1.asint = tca9555_i2c->interface.read_byte(tca9555_i2c, FALSE); + + tca9555_i2c->interface.stop(tca9555_i2c); +} +void tca9555_write_struct(tca9555_register *reg, tca9555_reg_t reg_num) +{ + uint8_t reg_data[2]; + osel_memcpy(reg_data, (uint8_t *)reg, sizeof(tca9555_register)); + tca9555_i2c->interface.start(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, WRITE_ADDRESS); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + + tca9555_i2c->interface.write_byte(tca9555_i2c, reg_num); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, reg_data[0]); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.write_byte(tca9555_i2c, reg_data[1]); + tca9555_i2c->interface.wait_ack(tca9555_i2c); + tca9555_i2c->interface.stop(tca9555_i2c); +} + +void tca9555_write_output(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_OUTPUT_REG0); +} +void tca9555_write_polarity(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_POLARITY_REG0); +} +void tca9555_write_config(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_CONFIG_REG0); +} + +void tca9555_read_input(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_INPUT_REG0); +} +void tca9555_read_output(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_OUTPUT_REG0); +} +void tca9555_read_polarity(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_POLARITY_REG0); +} +void tca9555_read_config(tca9555_register *reg) +{ + tca9555_write_struct(reg, TCA9555_CONFIG_REG0); +} diff --git a/tca9555.h b/tca9555.h new file mode 100644 index 0000000..5a102e2 --- /dev/null +++ b/tca9555.h @@ -0,0 +1,78 @@ +#ifndef __TCA9555_H__ +#define __TCA9555_H__ +#include "main.h" +#include "lib.h" + +#define TCA9555_ADDRESS 0x20 /*!< I2C Address */ + +/************************** I2C Registers *************************************/ +typedef enum +{ + TCA9555_INPUT_REG0 = 0x00, /*!< 输入状态寄存器 */ + TCA9555_INPUT_REG1 = 0x01, /*!< 输入状态寄存器 */ + TCA9555_OUTPUT_REG0 = 0x02, /*!< 输出寄存器以更改输出状态的位。设置为1时,设置为高电平 */ + TCA9555_OUTPUT_REG1 = 0x03, /*!< 输出寄存器以更改输出状态的位。设置为1时,设置为高电平 */ + TCA9555_POLARITY_REG0 = 0x04, /*!< 极性反转寄存器。位'1'在寄存器0x00上反转输入极性 */ + TCA9555_POLARITY_REG1 = 0x05, /*!< 极性反转寄存器。位'1'在寄存器0x00上反转输入极性 */ + TCA9555_CONFIG_REG0 = 0x06, /*!< 配置寄存器。位'1'设置为输入,位'0'设置为输出 */ + TCA9555_CONFIG_REG1 = 0x07 /*!< 配置寄存器。位'1'设置为输入,位'0'设置为输出 */ +} tca9555_reg_t; + +#define CONFIG_INPUT_VAL 0xFF +#define CONFIG_OUTPUT_VAL 0x00 + +#define WRITE_ADDRESS ((TCA9555_ADDRESS << 1) | 0) /*!< I2C主设备写入 */ +#define READ_ADDRESS ((TCA9555_ADDRESS << 1) | 1) /*!< I2C主设备读取 */ +#define ACK_CHECK_EN 0x1 /*!< I2C主设备检查从设备ACK */ +#define ACK_CHECK_DIS 0x0 /*!< I2C主设备不检查从设备ACK */ +#define ACK_VAL 0x0 /*!< I2C从设备ACK值 */ +#define NACK_VAL 0x1 /*!< I2C从设备NACK值 */ + +struct tca9555_sbit +{ + uint8_t b0 : 1; + uint8_t b1 : 1; + uint8_t b2 : 1; + uint8_t b3 : 1; + uint8_t b4 : 1; + uint8_t b5 : 1; + uint8_t b6 : 1; + uint8_t b7 : 1; +}; + +union tca9555_uinputport +{ + uint8_t asint; /*!< port data as unsigned integer */ + struct tca9555_sbit bit; /*!< port data as separate bits */ +}; + +struct tca9555_sregister +{ + union tca9555_uinputport p0; + union tca9555_uinputport p1; +}; + +typedef union +{ + uint16_t asint; /*!< register data as unsigned integer */ + struct tca9555_sregister port; /*!< register data as separate ports */ +} tca9555_register; + +void tca9555_init(void); // 初始化i2c总线 +void tca9555_configure_input(tca9555_register *reg, uint8_t port, uint8_t pin); // 配置输入 +uint8_t tca9555_read_single_register(tca9555_reg_t address); // 读取单个寄存器 +void tca9555_write_single_register(tca9555_reg_t address, uint8_t reg_val); // 写入单个寄存器 + +void tca9555_read_struct(tca9555_register *reg, tca9555_reg_t reg_num); // 读取结构体 +void tca9555_write_struct(tca9555_register *reg, tca9555_reg_t reg_num); // 写入结构体 + +void tca9555_write_output(tca9555_register *reg); +void tca9555_write_polarity(tca9555_register *reg); +void tca9555_write_config(tca9555_register *reg); + +void tca9555_read_input(tca9555_register *reg); +void tca9555_read_output(tca9555_register *reg); +void tca9555_read_polarity(tca9555_register *reg); +void tca9555_read_config(tca9555_register *reg); + +#endif // __TCA9555_H__ diff --git a/tmc2240.c b/tmc2240.c new file mode 100644 index 0000000..5221d18 --- /dev/null +++ b/tmc2240.c @@ -0,0 +1,368 @@ +#include "tmc2240.h" + +tmc2240_t _tmc2240[TMC2240_MAX]; + +static void tmc2240_write(tmc2240_index_e index, uint8_t *data) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + tmc->spi->interface.u.normal.spi_send(tmc->spi, data[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); +} + +static void tmc2240_read(tmc2240_index_e index, uint8_t *wdata, uint8_t *rdata) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); + + __NOP(); + __NOP(); + __NOP(); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); +} + +static void tmc2240_reg_write(tmc2240_index_e index, uint8_t reg, uint32_t data) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + uint8_t wdata[5] = {0}; + wdata[0] = TMC2240_HIGHT_BIT | reg; + wdata[1] = (data >> 24) & 0xFF; + wdata[2] = (data >> 16) & 0xFF; + wdata[3] = (data >> 8) & 0xFF; + wdata[4] = data & 0xFF; + tmc2240_write(index, wdata); +} + +static uint32_t tmc2240_reg_read(tmc2240_index_e index, uint8_t reg) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + uint8_t wdata[5] = {0}; + uint8_t rdata[5] = {0}; + wdata[0] = reg; + tmc2240_read(index, wdata, rdata); + return (rdata[1] << 24) | (rdata[2] << 16) | (rdata[3] << 8) | rdata[4]; +} + +/** + * @brief 配置TMC2240步进电机驱动器 + * + * 该函数用于配置TMC2240步进电机驱动器的各种参数,包括步进电机的工作模式、加减速曲线等。 + * + * @param index TMC2240步进电机驱动器的索引,用于指定要配置的驱动器。 + */ +static void tmc2240_config_write(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + tmc->config.gconf.data = 0x00000000; + + tmc->config.chopconf.data = 0x00410153; + tmc->config.chopconf.bits.mres = TMC2240_MRES_8; + + tmc->config.drvconf.data = 0x00000021; + tmc->config.global_scaler.data = 0x00000000; + + tmc->config.ihold_irun.bits.ihold = 31; + tmc->config.ihold_irun.bits.irun = 31; + tmc->config.ihold_irun.bits.iholddelay = 0; + tmc->config.ihold_irun.bits.irundelay = 0; + + tmc->config.pwmconf.data = 0xC44C261E; + tmc->config.gstat.data = 0x00000007; + tmc2240_reg_write(index, TMC2240_GCONF, tmc->config.gconf.data); + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + tmc2240_reg_write(index, TMC2240_DRV_CONF, tmc->config.drvconf.data); + tmc2240_reg_write(index, TMC2240_GLOBAL_SCALER, tmc->config.global_scaler.data); + tmc2240_reg_write(index, TMC2240_IHOLD_IRUN, tmc->config.ihold_irun.data); + tmc2240_reg_write(index, TMC2240_PWMCONF, tmc->config.pwmconf.data); + tmc2240_reg_write(index, TMC2240_GSTAT, tmc->config.gstat.data); +} + +static void _tmc2240_motor_update(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + switch (tmc->config.chopconf.bits.mres) + { + case TMC2240_MRES_256: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 256; + break; + case TMC2240_MRES_128: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 128; + break; + case TMC2240_MRES_64: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 64; + break; + case TMC2240_MRES_32: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 32; + break; + case TMC2240_MRES_16: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 16; + break; + case TMC2240_MRES_8: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 8; + break; + case TMC2240_MRES_4: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 4; + break; + case TMC2240_MRES_2: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 2; + break; + case TMC2240_MRES_1: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE; + break; + default: + break; + } + + tmc->motor.circle_pulse = 360 / tmc->motor.step_angle; +} + +static void _tmc2240_enable(tmc2240_index_e index, BOOL enable) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + BOOL state = tmc->en->read(*tmc->en) == 0 ? TRUE : FALSE; + + if (state == enable) + { + return; + } + + if (enable == TRUE) + { + PWM_START(tmc->timer, tmc->time_ch); + tmc->en->reset(*tmc->en); + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + } + else + { + PWM_STOP(tmc->timer, tmc->time_ch); + tmc->en->set(*tmc->en); + chopconf_u chopconf; + osel_memset((uint8_t *)&chopconf, 0, sizeof(chopconf_u)); + chopconf.bits.mres = TMC2240_MRES_1; + tmc2240_reg_write(index, TMC2240_CHOPCONF, chopconf.data); + } +} + +static void _tmc2240_direction(tmc2240_index_e index, tmc2240_direction_e dir) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (dir == TMC2240_FORWARD) + { + tmc->dir->reset(*tmc->dir); + } + else + { + tmc->dir->set(*tmc->dir); + } +} + +void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + osel_memset((uint8_t *)tmc, 0, sizeof(tmc2240_t)); + + tmc->timer = timer; + tmc->time_ch = time_ch; + tmc->spi = spi_create(SPI_TYPE_NORMAL, *gpios, 0); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + tmc->spi->interface.hardware_enable(tmc->spi, SPIx); + { + tmc->default_tm.sysclk = SystemCoreClock / 1000; + tmc->default_tm.psc = PWM_GET_PSC(tmc->timer); + tmc->default_tm.arr = PWM_GET_ARR(tmc->timer); + tmc->default_tm.freq = PWM_GET_FREQ(tmc->timer); + } + tmc->params.percent = TMC2240_PWM_DUTY_DEFAULT; + tmc->params.arr = 0; + tmc->params.freq = 0; + tmc->params.enable = FALSE; + tmc->params.direction = TMC2240_FORWARD; + tmc2240_config_write(index); + _tmc2240_motor_update(index); +} + +tmc2240_t *tmc2240_get(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + return &_tmc2240[index]; +} + +void tmc2240_percent(tmc2240_index_e index, float32 percent) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + PWM_SET_DUTY(tmc->timer, tmc->time_ch, ABS(percent)); +} + +void tmc2240_config_read(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + tmc->read_config.gconf.data = tmc2240_reg_read(index, TMC2240_GCONF); + tmc->read_config.chopconf.data = tmc2240_reg_read(index, TMC2240_CHOPCONF); + tmc->read_config.drvconf.data = tmc2240_reg_read(index, TMC2240_DRV_CONF); + tmc->read_config.global_scaler.data = tmc2240_reg_read(index, TMC2240_GLOBAL_SCALER); + tmc->read_config.ihold_irun.data = tmc2240_reg_read(index, TMC2240_IHOLD_IRUN); + tmc->read_config.pwmconf.data = tmc2240_reg_read(index, TMC2240_PWMCONF); + tmc->read_config.gstat.data = tmc2240_reg_read(index, TMC2240_GSTAT); + tmc->data.tmc2240_adc_temp = tmc2240_reg_read(index, TMC2240_ADC_TEMP); + tmc->data.tmc2240_temperature = (float32)(tmc->data.tmc2240_adc_temp - 2038) / 7.7; +} + +void tmc2240_test(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + _tmc2240_enable(index, tmc->params.enable); + + if (tmc->params.enable == TRUE) + { + _tmc2240_direction(index, tmc->params.direction); + + if (PWM_GET_ARR(tmc->timer) != tmc->params.arr) + { + if (tmc->params.arr == 0) + { + tmc->params.arr = tmc->default_tm.arr; + } + PWM_SET_ARR(tmc->timer, tmc->params.arr); + tmc->params.freq = PWM_GET_FREQ(tmc->timer); + } + + tmc2240_percent(index, tmc->params.percent); + + if (tmc->config.chopconf.bits.mres != tmc->read_config.chopconf.bits.mres) + { + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + _tmc2240_motor_update(index); + } + } +} + +void tmc2240_motor_set_angle(tmc2240_index_e index, int32_t angle) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (angle == 0) + { + return; + } + + tmc->params.enable = FALSE; + _tmc2240_enable(index, tmc->params.enable); + tmc->motor.pulse_count = 0; + + if (angle > 0) + { + tmc->params.direction = TMC2240_FORWARD; + } + else + { + tmc->params.direction = TMC2240_BACKWARD; + } + + tmc->motor.pulse_count = ABS(angle) / tmc->motor.step_angle; + tmc->motor.step_angle_count = 0; + + tmc->params.enable = TRUE; + + _tmc2240_direction(index, tmc->params.direction); + if (PWM_GET_ARR(tmc->timer) != tmc->params.arr) + { + if (tmc->params.arr == 0) + { + tmc->params.arr = tmc->default_tm.arr; + } + PWM_SET_ARR(tmc->timer, tmc->params.arr); + tmc->params.freq = PWM_GET_FREQ(tmc->timer); + } + + tmc2240_percent(index, tmc->params.percent); + + if (tmc->config.chopconf.bits.mres != tmc->read_config.chopconf.bits.mres) + { + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + _tmc2240_motor_update(index); + } + _tmc2240_enable(index, tmc->params.enable); +} + +void tmc2240_motor_update(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (tmc->motor.pulse_count > 0) + { + tmc->motor.pulse_count--; // 脉冲数 + tmc->motor.step_angle_count++; // 步距个数 + if (tmc->params.direction == TMC2240_FORWARD) + { + tmc->motor.add_pulse_count++; /* 绝对位置++ */ + } + else + { + tmc->motor.add_pulse_count--; /* 绝对位置-- */ + } + } + + /* 当脉冲数等于0的时候 代表需要发送的脉冲个数已完成,停止输出 */ + + if (tmc->motor.pulse_count <= 0) + { + tmc->params.enable = FALSE; + _tmc2240_enable(index, tmc->params.enable); + } +} diff --git a/tmc2240.h b/tmc2240.h new file mode 100644 index 0000000..1f5379d --- /dev/null +++ b/tmc2240.h @@ -0,0 +1,372 @@ +/** + * @file tmc2240.h + * @author xushenghao + * @brief TMC2240驱动头文件 + * @version 0.1 + * @note + * 1. 芯片VM需要供电,否则SPI无法正常通信 + * 2. 42步进电机每步1.8度,1圈200步。1/8细分,1步=0.225度,电机转一周需要1600个脉冲 + */ + +#ifndef __TMC2240_H +#define __TMC2240_H +#include "main.h" +#include "spis.h" + +#define MOTOR_42_STEP_ANGLE 1.8f // 42步进电机每步1.8度 +#define TMC2240_PWM_DUTY_DEFAULT 50 // PWM默认占空比 + +/* +0x00 = 0x00002108 ;; writing GCONF @ address 0=0x00 with 0x00002108=8456=0.0 +0x03 = 0x00000000 ;; writing SLAVECONF @ address 1=0x03 with 0x00000000=0=0.0 +0x04 = 0x4001682C ;; writing IOIN @ address 2=0x04 with 0x4001682C=1073834028=0.0 +0x0A = 0x00000021 ;; writing DRV_CONF @ address 3=0x0A with 0x00000021=33=0.0 +0x0B = 0x00000000 ;; writing GLOBAL_SCALER @ address 4=0x0B with 0x00000000=0=0.0 +0x10 = 0x00001208 ;; writing IHOLD_IRUN @ address 5=0x10 with 0x00001208=4616=0.0 +0x11 = 0x00000000 ;; writing TPOWERDOWN @ address 6=0x11 with 0x00000000=0=0.0 +0x13 = 0x00000000 ;; writing TPWMTHRS @ address 7=0x13 with 0x00000000=0=0.0 +0x14 = 0x000003BE ;; writing TCOOLTHRS @ address 8=0x14 with 0x000003BE=958=0.0 +0x15 = 0x00000000 ;; writing THIGH @ address 9=0x15 with 0x00000000=0=0.0 +0x2D = 0x00000000 ;; writing DIRECT_MODE @ address 10=0x2D with 0x00000000=0=0.0 +0x38 = 0x00000000 ;; writing ENCMODE @ address 11=0x38 with 0x00000000=0=0.0 +0x39 = 0x00000000 ;; writing X_ENC @ address 12=0x39 with 0x00000000=0=0.0 +0x3A = 0x00010000 ;; writing ENC_CONST @ address 13=0x3A with 0x00010000=65536=0.0 +0x52 = 0x0B920F25 ;; writing OTW_OV_VTH @ address 14=0x52 with 0x0B920F25=194121509=0.0 +0x60 = 0xAAAAB554 ;; writing MSLUT[0] @ address 15=0x60 with 0xAAAAB554=0=0.0 +0x61 = 0x4A9554AA ;; writing MSLUT[1] @ address 16=0x61 with 0x4A9554AA=1251300522=0.0 +0x62 = 0x24492929 ;; writing MSLUT[2] @ address 17=0x62 with 0x24492929=608774441=0.0 +0x63 = 0x10104222 ;; writing MSLUT[3] @ address 18=0x63 with 0x10104222=269500962=0.0 +0x64 = 0xFBFFFFFF ;; writing MSLUT[4] @ address 19=0x64 with 0xFBFFFFFF=0=0.0 +0x65 = 0xB5BB777D ;; writing MSLUT[5] @ address 20=0x65 with 0xB5BB777D=0=0.0 +0x66 = 0x49295556 ;; writing MSLUT[6] @ address 21=0x66 with 0x49295556=1227445590=0.0 +0x67 = 0x00404222 ;; writing MSLUT[7] @ address 22=0x67 with 0x00404222=4211234=0.0 +0x68 = 0xFFFF8056 ;; writing MSLUTSEL @ address 23=0x68 with 0xFFFF8056=0=0.0 +0x69 = 0x00F70000 ;; writing MSLUTSTART @ address 24=0x69 with 0x00F70000=16187392=0.0 +0x6C = 0x00410153 ;; writing CHOPCONF @ address 25=0x6C with 0x00410153=4260179=0.0 +0x6D = 0x00040000 ;; writing COOLCONF @ address 26=0x6D with 0x00040000=262144=0.0 +0x70 = 0xC44C001E ;; writing PWMCONF @ address 27=0x70 with 0xC44C001E=0=0.0 +0x74 = 0x00000000 ;; writing SG4_THRS @ address 28=0x74 with 0x00000000=0=0.0 +*/ + +#define TMC2240_GCONF 0x00 +#define TMC2240_GSTAT 0x01 +#define TMC2240_IFCNT 0x02 +#define TMC2240_SLAVECONF 0x03 +#define TMC2240_IOIN 0x04 +#define TMC2240_DRV_CONF 0x0A +#define TMC2240_GLOBAL_SCALER 0x0B + +#define TMC2240_IHOLD_IRUN 0x10 +#define TMC2240_TPOWERDOWN 0x11 +#define TMC2240_TSTEP 0x12 +#define TMC2240_TPWMTHRS 0x13 +#define TMC2240_TCOOLTHRS 0x14 +#define TMC2240_THIGH 0x15 + +#define TMC2240_DIRECT_MODE 0x2D + +#define TMC2240_ENCMODE 0x38 +#define TMC2240_XENC 0x39 +#define TMC2240_ENC_CONST 0x3A +#define TMC2240_ENC_STATUS 0x3B +#define TMC2240_ENC_LATCH 0x3C + +#define TMC2240_ADC_VSUPPLY_AIN 0x50 +#define TMC2240_ADC_TEMP 0x51 +#define TMC2240_OTW_OV_VTH 0x52 + +#define TMC2240_MSLUT0 0x60 +#define TMC2240_MSLUT1 0x61 +#define TMC2240_MSLUT2 0x62 +#define TMC2240_MSLUT3 0x63 +#define TMC2240_MSLUT4 0x64 +#define TMC2240_MSLUT5 0x65 +#define TMC2240_MSLUT6 0x66 +#define TMC2240_MSLUT7 0x67 +#define TMC2240_MSLUTSEL 0x68 +#define TMC2240_MSLUTSTART 0x69 +#define TMC2240_MSCNT 0x6A +#define TMC2240_MSCURACT 0x6B +#define TMC2240_CHOPCONF 0x6C +#define TMC2240_COOLCONF 0x6D +#define TMC2240_DCCTRL 0x6E +#define TMC2240_DRVSTATUS 0x6F + +#define TMC2240_PWMCONF 0x70 +#define TMC2240_PWMSCALE 0x71 +#define TMC2240_PWM_AUTO 0x72 +#define TMC2240_SG4_THRS 0x74 +#define TMC2240_SG4_RESULT 0x75 +#define TMC2240_SG4_IND 0x76 + +#define TMC2240_HIGHT_BIT 0x80 + +typedef enum +{ + TMC2240_1, + TMC2240_MAX, +} tmc2240_index_e; + +typedef enum +{ + TMC2240_FORWARD, // 正转 + TMC2240_BACKWARD, // 反转 +} tmc2240_direction_e; + +typedef enum +{ + TMC2240_MRES_256, + TMC2240_MRES_128, + TMC2240_MRES_64, + TMC2240_MRES_32, + TMC2240_MRES_16, + TMC2240_MRES_8, + TMC2240_MRES_4, + TMC2240_MRES_2, + TMC2240_MRES_1, // FULL STEP +} tmc2240_mres_e; + +// 0x00 GCONF +typedef union +{ + uint32_t data; + struct + { + + uint32_t reserved1 : 1; + /** + * 停止之前的步骤执行超时检测。 + * 0x0:正常时间:2^20个时钟 + * 0x1:短时间:2^18个时钟 + */ + uint32_t fast_standstill : 1; + /** + * 启用StealthChop2模式。 + * 0x0:无StealthChop2 + * 0x1:StealthChop2电压PWM模式使能(取决于速度阈值)。从关闭状态切换 + 在静止状态下和在lHOLD = 时为开状态 仅限额定电流。 + */ + uint32_t en_pwm_mode : 1; + /** + * 启用StealthChop2的步进输入筛选 + * 0x0:无StealthChop2 + * 0x1:StealthChop2电压PWM模式使能 + (取决于速度阈值)。从关闭状态切换 + 在静止状态下和在lHOLD=时为开状态 + 仅限额定电流。 + */ + uint32_t multistep_filt : 1; + /** + * 更改电机方向/方向标志 + * 0x0:默认电机方向 + * 0x1:电机方向相反 + */ + uint32_t shaft : 1; + uint32_t diag0_error : 1; + uint32_t diag0_otpw : 1; + uint32_t diag0_stall : 1; + uint32_t diag1_stall : 1; + uint32_t diag1_index : 1; + uint32_t diag1_onstate : 1; + uint32_t reserved2 : 1; + uint32_t diag0_pushpull : 1; + uint32_t diag1_pushpull : 1; + uint32_t small_hysteresis : 1; + /** + * 电机硬停止功能启用。 + * 0x0:正常运行 + * 0x1:紧急停止:ENCA停止定序器 + 当绑得很高时(不执行任何步骤 + 定序器、电机进入停顿状态)。 + */ + uint32_t stop_enable : 1; + /** + * 通过以下方式启用直接motpr相电流控制 + * 0x0:正常运行 + * 0x1:电机线圈电流和极性直接 + 通过串口编程:寄存器 + 直接模式(0x2D)指定带符号线圈A + 电流(位8..0)和线圈B电流(位24..16)。在……里面 + 在此模式下,电流按lHOLD设置进行定标。 + 基于速度的StealthChop2电流调节 + 在此模式下不可用。自动的 + StealthChop2电流调节仅适用于 + 步进电机速度低。 + */ + uint32_t direct_mode : 1; + } bits; +} gconf_u; + +// 0x01 GSTAT +typedef union +{ + uint32_t data; + struct + { + uint32_t reset : 1; + uint32_t drv_err : 1; + uint32_t uv_cp : 1; + uint32_t register_reset : 1; + uint32_t vm_uvlo : 1; + } bits; +} gstat_u; + +// 0x0A DRVCONF +typedef union +{ + uint32_t data; + struct + { + uint32_t current_range : 2; // 0-1 + uint32_t reserved1 : 2; // 2-3 + uint32_t slope_control : 2; // 4-5 + } bits; +} drvconf_u; + +// 0x0B GLOBAL_SCALER +typedef union +{ + uint32_t data; + struct + { + uint32_t global_scale : 8; // 0-7 + } bits; +} global_scaler_u; + +// 0x10 IHOLD_IRUN +typedef union +{ + uint32_t data; + struct + { + uint32_t ihold : 5; // 0-4 + uint32_t reserved1 : 3; // 5-7 + uint32_t irun : 5; // 8-12 + uint32_t reserved2 : 3; // 13-15 + uint32_t iholddelay : 4; // 16-19 + uint32_t reserved3 : 4; // 20-23 + uint32_t irundelay : 4; // 24-27 + } bits; +} ihold_irun_u; + +// 0x6C CHOPCONF +typedef union +{ + uint32_t data; + struct + { + uint32_t toff : 4; // 0-3 + uint32_t hstrt : 3; // 4-6 + uint32_t hend : 4; // 7-10 + uint32_t fd3 : 1; // 11 + uint32_t disfdcc : 1; // 12 + uint32_t reserved1 : 1; + uint32_t chm : 1; // 14 + uint32_t tbl : 2; // 15-16 + uint32_t reserved2 : 1; + uint32_t vhighfs : 1; // 18 + uint32_t vhighchm : 1; // 19 + uint32_t tpfd : 4; // 20-23 + uint32_t mres : 4; // 24-27 + uint32_t intpol : 1; // 28 + uint32_t dedge : 1; // 29 + uint32_t diss2g : 1; // 30 + uint32_t diss2vs : 1; // 31 + } bits; +} chopconf_u; + +// 0x70 PWMCONF +typedef union +{ + uint32_t data; + struct + { + /** + * 用户定义的 PWM 幅度偏移(0-255)与静止状态下的全电机电流(CS_ACTUAL=31)相关。 (重置默认值=30) 使用 PWM_OFS 作为自动缩放的初始值, + * 以加快自动调谐过程。为此,请将 PWM_OFS 设置为确定的、特定于应用的值,并将 pwm_autoscale 设置为 0。之后,将 pwm_autoscale 设置为 1。 + * 完成后启用 StealthChop2。 PWM_OFS = 0 将禁用将电机电流缩放到低于电机特定的较低测量阈值。此设置应仅在某些条件下使用, + * 例如当电源电压可以上下变化两倍或更多时。它可以防止电机超出调节范围,但也会防止电流降到调节限制以下。 PWM_OFS > 0 允许自动缩放到低 PWM 占空比, + * 甚至低于较低的调节阈值。这允许基于实际(保持)电流比例(寄存器 IHOLD_IRUN)的低(静止)电流设置。 + */ + uint32_t pwm_ofs : 8; + /** + * PWM 幅度的速度依赖梯度: PWM_GRAD x 256 / TSTEP 此值加到 PWM_OFS 以补偿速度依赖的电机反电动势。 使用 PWM_GRAD 作为自动缩放的初始值, + * 以加快自动调谐过程。为此,请将 PWM_GRAD 设置为确定的、特定于应用的值,并将 pwm_autoscale 设置为 0。之后,将 pwm_autoscale 设置为 1。 + * 完成后启用 StealthChop2。 提示: 初始调谐后,可以从 PWM_GRAD_AUTO 中读取所需的初始值。 + */ + uint32_t pwm_grad : 8; + uint32_t pwm_freq : 2; + uint32_t pwm_autoscale : 1; + uint32_t pwm_autograd : 1; + } bits; +} pwmconf_u; + +typedef struct +{ + gconf_u gconf; // 0x00 GCONF + gstat_u gstat; // 0x01 GSTAT + drvconf_u drvconf; // 0x0A DRVCONF + global_scaler_u global_scaler; // 0x0B GLOBAL_SCALER + ihold_irun_u ihold_irun; // 0x10 IHOLD_IRUN + chopconf_u chopconf; // 0x6C CHOPCONF + pwmconf_u pwmconf; // 0x70 PWMCONF +} tmc2240_config_t; + +typedef struct +{ + gpio_t *en; ///< EN_PIN + gpio_t *dir; ///< DIR_PIN + TIM_TypeDef *timer; + uint32_t time_ch; + spi_t *spi; + + tmc2240_config_t config; + tmc2240_config_t read_config; + + uint32_t step; + __IO uint32_t step_count; + + // PRIVATE + struct + { + uint32_t sysclk; // 系统时钟 + uint32_t psc; // 预分频系数 + uint16_t arr; // 自动重装值 auto reload value + uint32_t freq; // 频率 + } default_tm; + struct + { + BOOL enable; // 使能 + tmc2240_direction_e direction; // 方向 + float32 percent; // 占空比 + uint16_t arr; // 自动重装值(改变速度) + uint32_t freq; // 频率 + } params; + + struct + { + float32 step_angle; // 步进角度 + uint16_t circle_pulse; // 一圈脉冲数 + + __IO int32_t add_pulse_count; /* 脉冲个数累计 */ + __IO uint32_t pulse_count; /* 脉冲个数记录 */ + __IO uint32_t step_angle_count; /* 步距个数 */ + } motor; + + struct + { + uint16_t tmc2240_adc_temp; // 温度ADC值 + float32 tmc2240_temperature; // 温度 + } data; + +} tmc2240_t; + +void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios); +tmc2240_t *tmc2240_get(tmc2240_index_e index); + +void tmc2240_motor_set_angle(tmc2240_index_e index, int32_t angle); +void tmc2240_motor_update(tmc2240_index_e index); + +void tmc2240_test(tmc2240_index_e index); +void tmc2240_config_read(tmc2240_index_e index); +#endif // __TMC2240_H