Attiny1616中如何存储float计算结果到FLASH?PROGMEM及转char尝试失败
解决Attiny1616将float存入FLASH的问题
一、直接存储float到FLASH(PROGMEM支持float)
你之前误以为PROGMEM不支持float是误解,它可以存储任意数据类型,只要声明和读取方式正确。
声明并存储float到PROGMEM
直接用PROGMEM修饰float变量,需声明为const(FLASH为只读存储):
#include <avr/io.h> #include <avr/pgmspace.h> int main(void) { // 编译时将float存入FLASH const float cal_result PROGMEM = 10.83; // 读取FLASH中的float值 float read_value = pgm_read_float_near(&cal_result); while(1){ } }
Attiny1616的FLASH仅16KB,用pgm_read_float_near即可;若芯片FLASH超过64KB,才需要用pgm_read_float_far。
二、存储字符串形式的float到FLASH
你之前的代码问题在于:out是编译时初始化的PROGMEM数组,但buf是运行时赋值的,编译时buf内容不确定,无法用于初始化PROGMEM。分两种场景处理:
场景1:编译时已知float值
直接将字符串常量定义到PROGMEM:
#include <avr/io.h> #include <avr/pgmspace.h> #include <string.h> int main(void) { // 编译时将float的字符串形式存入FLASH const char float_str[] PROGMEM = "10.83"; // 读取FLASH中的字符串 char buf[6]; strcpy_P(buf, float_str); // 使用strcpy_P读取PROGMEM字符串 while(1){ } }
场景2:运行时计算得到的float,需存字符串到FLASH
这种情况无法用PROGMEM(PROGMEM是编译时分配的),必须使用AVR的**自编程(Self-Programming)**功能直接写入FLASH。Attiny1616支持该功能,示例代码如下:
#include <avr/io.h> #include <avr/pgmspace.h> #include <avr/boot.h> #include <avr/interrupt.h> #include <stdio.h> // Attiny1616共16KB FLASH,每页128字节,最后一页起始地址为0x3C00 #define FLASH_TARGET_PAGE 0x3C00 void write_str_to_flash(const char* str) { uint16_t addr = FLASH_TARGET_PAGE; uint8_t idx = 0; cli(); // 禁用全局中断,避免写入过程被打断 // 擦除目标FLASH页 boot_page_erase(addr); boot_spm_busy_wait(); // 等待擦除完成 // 按字写入FLASH(FLASH以16位字为单位写入) while (str[idx] != '\0' && addr < FLASH_TARGET_PAGE + SPM_PAGESIZE) { uint16_t write_word = str[idx]; if (str[idx+1] != '\0') { write_word |= ((uint16_t)str[idx+1]) << 8; idx++; } boot_page_fill(addr, write_word); addr += 2; idx++; } // 提交写入操作 boot_page_write(FLASH_TARGET_PAGE); boot_spm_busy_wait(); sei(); // 恢复全局中断 } int main(void) { char buf[6]; float cal_result = 10.83; // 将float转换为字符串 snprintf(buf, 6, "%.2f", cal_result); // 写入FLASH write_str_to_flash(buf); // 读取验证 char read_buf[6] = {0}; uint16_t read_addr = FLASH_TARGET_PAGE; uint8_t read_idx = 0; while (read_idx < 5) { uint16_t read_word = pgm_read_word(read_addr); read_buf[read_idx++] = read_word & 0xFF; read_buf[read_idx++] = (read_word >> 8) & 0xFF; read_addr += 2; } while(1){ } }
注意:自编程需确保芯片熔丝位的SPMEN位已使能(Attiny1616默认使能),且写入地址不能覆盖当前运行的程序代码,建议选择FLASH末尾页。
总结
- 编译时确定的float:直接用
PROGMEM修饰,通过pgm_read_float_near读取 - 编译时确定的字符串形式float:定义
const char[] PROGMEM,用strcpy_P读取 - 运行时计算的float:需存字符串到FLASH时,必须使用自编程API写入
内容的提问来源于stack exchange,提问作者Takeru Tsuji
相关产品推荐
相关产品推荐

