; =============================================
; 23LC1024 SRAM测试程序 - ASM完整版
; =功能:完整的SRAM读写测试,包含UART输出和LED指示
; =============================================
.include "m16def.inc" ; 包含ATmega16定义文件
; 系统常量定义
.equ F_CPU = 1000000 ; CPU频率 1MHz
.equ SRAM_CS = PB4 ; SRAM片选引脚(PB4)
.equ MOSI_PIN = PB5 ; SPI主出从入引脚(PB5)
.equ MISO_PIN = PB6 ; SPI主入从出引脚(PB6)
.equ SCK_PIN = PB7 ; SPI时钟引脚(PB7)
.equ CMD_READ = 0x03 ; SRAM读命令
.equ CMD_WRITE = 0x02 ; SRAM写命令
.equ CMD_RDSR = 0x05 ; 读状态寄存器命令
.equ CMD_WRSR = 0x01 ; 写状态寄存器命令
.equ MODE_SEQ = 0x40 ; 顺序模式配置值
.equ UBRR_VAL = 12 ; 波特率9600 @ 1MHz
; 数据段定义(SRAM变量)
.dseg
write_data: .byte 1 ; 写入数据存储
read_data: .byte 1 ; 读取数据存储
addr_high: .byte 1 ; 24位地址高字节
addr_mid: .byte 1 ; 24位地址中字节
addr_low: .byte 1 ; 24位地址低字节
temp: .byte 1 ; 临时变量
test_index: .byte 1 ; 测试用例计数器
test_offset: .byte 1 ; 测试表偏移量
; 代码段定义
.cseg
.org 0x0000 ; 程序起始地址
rjmp main ; 复位向量跳转到主程序
; 测试数据表 - 7个测试用例(地址+数据)
; 格式:地址高字节, 地址中字节, 地址低字节, 测试数据
test_table:
.db 0x10, 0x00, 0x00, 0x11 ; 测试1: 地址 0x100000, 数据 0x11
.db 0x20, 0x00, 0x00, 0x22 ; 测试2: 地址 0x200000, 数据 0x22
.db 0x00, 0x01, 0x00, 0x33 ; 测试3: 地址 0x000100, 数据 0x33
.db 0x00, 0x00, 0x10, 0x44 ; 测试4: 地址 0x000010, 数据 0x44
.db 0x30, 0x20, 0x10, 0x55 ; 测试5: 地址 0x302010, 数据 0x55
.db 0x50, 0x20, 0x10, 0x65 ; 测试5: 地址 0x302010, 数据 0x65
.db 0x80, 0x20, 0x10, 0x81 ; 测试5: 地址 0x302010, 数据 0x81
; 消息字符串定义(中英双语注释)
msg_title: .db "23LC1024 ASM 完整测试",13,10,0 ; 程序标题
msg_init: .db "SRAM 已初始化",13,10,0 ; SRAM初始化完成
msg_test: .db "正在运行完整测试...",13,10,0 ; 测试开始提示
msg_ok: .db " [通过]",13,10,0 ; 测试通过
msg_fail: .db " [失败]",13,10,0 ; 测试失败
msg_addr: .db "Addr 0x",0 ; 地址显示前缀
msg_write: .db " W=0x",0 ; 写入数据显示前缀
msg_read: .db " R=0x",0 ; 读取数据显示前缀
msg_crlf: .db 13,10,0,0 ; 回车换行
msg_done: .db "=== 所有测试已完成! ===",13,10,0 ; 测试完成提示
; =============================================
; 主程序
; =系统初始化和测试流程控制
; =============================================
main:
; 初始化堆栈指针
ldi r16, high(RAMEND) ; 加载RAM结束地址高字节
out SPH, r16 ; 设置堆栈指针高字节
ldi r16, low(RAMEND) ; 加载RAM结束地址低字节
out SPL, r16 ; 设置堆栈指针低字节
rcall uart_init ; 初始化UART串口
; 显示程序标题
ldi ZL, low(2*msg_title) ; 加载标题字符串地址低字节
ldi ZH, high(2*msg_title) ; 加载标题字符串地址高字节
rcall uart_puts ; 发送标题字符串
rcall uart_crlf ; 发送回车换行
; 初始化SRAM
rcall sram_init ; 初始化SPI和SRAM
; 显示初始化完成信息
ldi ZL, low(2*msg_init) ; 加载初始化消息地址低字节
ldi ZH, high(2*msg_init) ; 加载初始化消息地址高字节
rcall uart_puts ; 发送初始化消息
; 显示测试开始信息
ldi ZL, low(2*msg_test) ; 加载测试消息地址低字节
ldi ZH, high(2*msg_test) ; 加载测试消息地址高字节
rcall uart_puts ; 发送测试消息
rcall uart_crlf ; 发送回车换行
; 设置测试用例数量
ldi r16, 7 ; 设置总共运行5个测试用例
sts test_index, r16 ; 保存测试计数器
ldi r17, 0 ; 初始化测试表偏移量为0
sts test_offset, r17 ; 保存偏移量
; 测试循环主程序
run_tests:
; 设置Z指针指向测试参数表的起始地址
ldi ZL, low(2*test_table) ; 加载测试表地址低字节
ldi ZH, high(2*test_table) ; 加载测试表地址高字节
; 加上当前测试的偏移量以定位到具体测试用例
lds r17, test_offset ; 加载当前偏移量
add ZL, r17 ; 加到Z指针低字节
ldi r18, 0 ; 清零用于带进位加法
adc ZH, r18 ; 加到Z指针高字节(带进位)
; 从程序存储器加载测试参数
lpm r16, Z+ ; 加载地址高字节并递增指针
sts addr_high, r16 ; 保存地址高字节
lpm r16, Z+ ; 加载地址中字节并递增指针
sts addr_mid, r16 ; 保存地址中字节
lpm r16, Z+ ; 加载地址低字节并递增指针
sts addr_low, r16 ; 保存地址低字节
lpm r16, Z+ ; 加载测试数据并递增指针
sts write_data, r16 ; 保存要写入的数据
; 执行SRAM读写测试
rcall sram_write_byte ; 向SRAM写入测试数据
rcall sram_read_byte ; 从SRAM读取数据
rcall display_test_result ; 显示测试结果
; 更新偏移量指向下一个测试用例(每个测试4个字节参数)
lds r17, test_offset ; 加载当前偏移量
subi r17, -4 ; 偏移量加4(指向下一个测试)
sts test_offset, r17 ; 保存更新后的偏移量
; 检查是否完成所有测试
lds r16, test_index ; 加载剩余测试计数
dec r16 ; 计数器减1
sts test_index, r16 ; 保存更新后的计数
brne run_tests ; 如果未完成,继续下一个测试
; 所有测试完成,显示结束信息
ldi ZL, low(2*msg_done) ; 加载完成消息地址低字节
ldi ZH, high(2*msg_done) ; 加载完成消息地址高字节
rcall uart_puts ; 发送完成消息
; 成功指示 - 闪烁LED (如果PC0连接了LED)
sbi DDRC, 0 ; 设置PC0为输出(连接LED)
success:
sbi PORTC, 0 ; 点亮LED(PC0输出高电平)
ldi r16, 100 ; 设置延时100ms
rcall delay_ms ; 调用延时子程序
cbi PORTC, 0 ; 熄灭LED(PC0输出低电平)
ldi r16, 100 ; 设置延时100ms
rcall delay_ms ; 调用延时子程序
rjmp success ; 无限循环闪烁LED
; =============================================
; 显示测试结果
; =功能:显示SRAM测试的详细结果和验证状态
; =============================================
display_test_result:
; 显示地址信息
ldi ZL, low(2*msg_addr) ; 加载"地址:"字符串地址低字节
ldi ZH, high(2*msg_addr) ; 加载"地址:"字符串地址高字节
rcall uart_puts ; 发送地址标签字符串
; 显示24位地址(高字节→中字节→低字节)
lds r16, addr_high ; 加载地址高字节
rcall uart_puthex ; 以十六进制显示高字节
lds r16, addr_mid ; 加载地址中字节
rcall uart_puthex ; 以十六进制显示中字节
lds r16, addr_low ; 加载地址低字节
rcall uart_puthex ; 以十六进制显示低字节
; 显示写入的数据
ldi ZL, low(2*msg_write) ; 加载"写入:"字符串地址低字节
ldi ZH, high(2*msg_write) ; 加载"写入:"字符串地址高字节
rcall uart_puts ; 发送写入标签字符串
lds r16, write_data ; 加载之前写入的数据
rcall uart_puthex ; 以十六进制显示写入的数据
; 显示读取的数据
ldi ZL, low(2*msg_read) ; 加载"读取:"字符串地址低字节
ldi ZH, high(2*msg_read) ; 加载"读取:"字符串地址高字节
rcall uart_puts ; 发送读取标签字符串
lds r16, read_data ; 加载从SRAM读取的数据
rcall uart_puthex ; 以十六进制显示读取的数据
; 验证测试结果:比较写入和读取的数据
lds r17, write_data ; 重新加载写入的数据到r17
lds r18, read_data ; 加载读取的数据到r18
cp r17, r18 ; 比较两个数据(r17 - r18)
breq test_ok ; 如果相等(Z=1),跳转到成功处理
; 测试失败处理
; 写入和读取的数据不匹配
ldi ZL, low(2*msg_fail) ; 加载"失败"字符串地址低字节
ldi ZH, high(2*msg_fail) ; 加载"失败"字符串地址高字节
rcall uart_puts ; 发送失败信息
ret ; 返回主程序
; 测试成功处理
test_ok:
; 写入和读取的数据完全匹配
ldi ZL, low(2*msg_ok) ; 加载"成功"字符串地址低字节
ldi ZH, high(2*msg_ok) ; 加载"成功"字符串地址高字节
rcall uart_puts ; 发送成功信息
ret ; 返回主程序
; =============================================
; UART子程序
; UART串口通信相关功能子程序
; =============================================
; UART初始化子程序
; 功能:设置UART波特率、数据帧格式和使能发送
uart_init:
ldi r16, low(UBRR_VAL) ; 加载波特率分频值的低字节
out UBRRL, r16 ; 写入波特率低字节寄存器
ldi r16, high(UBRR_VAL) ; 加载波特率分频值的高字节
out UBRRH, r16 ; 写入波特率高字节寄存器
ldi r16, (1<<TXEN) ; 设置TXEN位(发送使能)
out UCSRB, r16 ; 写入UART控制状态寄存器B
ldi r16, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0) ; 设置URSEL(选择UCSRC)、8位数据位
out UCSRC, r16 ; 写入UART控制状态寄存器C
ret ; 返回
; 发送单个字符子程序
; 输入:r16 - 要发送的字符
uart_putchar:
sbis UCSRA, UDRE ; 跳过下条指令如果UDR空(UDRE=1)
rjmp uart_putchar ; UDR未空,继续等待
out UDR, r16 ; UDR已空,发送字符
ret ; 返回
; 发送字符串子程序
; 输入:Z指针 - 指向程序存储器中的字符串(以null结尾)
uart_puts:
lpm r16, Z+ ; 从程序存储器加载字符并递增Z指针
tst r16 ; 测试字符是否为0
breq uart_puts_end ; 如果是0,字符串结束
rcall uart_putchar ; 发送当前字符
rjmp uart_puts ; 继续发送下一个字符
uart_puts_end:
ret ; 返回
; 发送回车换行子程序
; 功能:发送CR(回车)和LF(换行)序列
uart_crlf:
ldi ZL, low(2*msg_crlf) ; 加载回车换行字符串地址低字节
ldi ZH, high(2*msg_crlf) ; 加载回车换行字符串地址高字节
rcall uart_puts ; 发送回车换行字符串
ret ; 返回
; 发送十六进制数字子程序
; 输入:r16 - 要发送的字节(以十六进制形式发送两个字符)
uart_puthex:
push r16 ; 保存原始值
swap r16 ; 交换高低4位
andi r16, 0x0F ; 屏蔽高4位,保留低4位
rcall hex_to_ascii ; 转换为ASCII字符
rcall uart_putchar ; 发送高4位对应的字符
pop r16 ; 恢复原始值
andi r16, 0x0F ; 屏蔽高4位,保留低4位
rcall hex_to_ascii ; 转换为ASCII字符
rcall uart_putchar ; 发送低4位对应的字符
ret ; 返回
; 十六进制到ASCII转换子程序
; 输入:r16 - 十六进制数字(0-15)
; 输出:r16 - 对应的ASCII字符
hex_to_ascii:
cpi r16, 10 ; 比较数字是否小于10
brlo hex_digit ; 如果小于10,跳转到数字处理
subi r16, -('A'-10) ; 大于等于10,转换为'A'-'F'
ret ; 返回
hex_digit:
subi r16, -'0' ; 小于10,转换为'0'-'9'
ret ; 返回
; =============================================
; SPI子程序
; =SPI总线通信和SRAM操作相关子程序
; =============================================
; SPI初始化子程序
; 功能:配置SPI引脚方向和SPI控制寄存器
spi_init:
sbi DDRB, MOSI_PIN ; 设置MOSI引脚为输出
sbi DDRB, SCK_PIN ; 设置SCK时钟引脚为输出
sbi DDRB, SRAM_CS ; 设置SRAM片选引脚为输出
cbi DDRB, MISO_PIN ; 设置MISO引脚为输入
sbi PORTB, SRAM_CS ; 拉高SRAM片选(不选中)
ldi r16, (1<<SPE)|(1<<MSTR)|(1<<SPR0) ; 使能SPI、主机模式、时钟分频
out SPCR, r16 ; 写入SPI控制寄存器
ret ; 返回
; SPI数据传输子程序
; 功能:通过SPI发送和接收一个字节
; 输入:r16 - 要发送的数据
; 输出:r16 - 接收到的数据
spi_transfer:
out SPDR, r16 ; 启动SPI传输,写入数据到SPI数据寄存器
spi_wait:
in r16, SPSR ; 读取SPI状态寄存器
sbrs r16, SPIF ; 跳过下条指令如果传输完成(SPIF=1)
rjmp spi_wait ; 传输未完成,继续等待
in r16, SPDR ; 传输完成,读取接收到的数据
ret ; 返回
; SRAM初始化子程序
; 功能:初始化SPI接口并配置SRAM工作模式
sram_init:
rcall spi_init ; 初始化SPI接口
cbi PORTB, SRAM_CS ; 拉低SRAM片选(选中芯片)
ldi r16, CMD_WRSR ; 加载写状态寄存器命令
rcall spi_transfer ; 发送命令
ldi r16, MODE_SEQ ; 加载顺序模式配置值
rcall spi_transfer ; 发送模式配置
sbi PORTB, SRAM_CS ; 拉高SRAM片选(取消选中)
ldi r16, 10 ; 设置延时10ms
rcall delay_ms ; 调用延时子程序
ret ; 返回
; SRAM写字节子程序
; 功能:向SRAM指定地址写入一个字节数据
; 输入:addr_high, addr_mid, addr_low - 24位地址
; write_data - 要写入的数据
sram_write_byte:
cbi PORTB, SRAM_CS ; 拉低SRAM片选(选中芯片)
ldi r16, CMD_WRITE ; 加载写命令
rcall spi_transfer ; 发送写命令
lds r16, addr_high ; 加载地址高字节
rcall spi_transfer ; 发送地址高字节
lds r16, addr_mid ; 加载地址中字节
rcall spi_transfer ; 发送地址中字节
lds r16, addr_low ; 加载地址低字节
rcall spi_transfer ; 发送地址低字节
lds r16, write_data ; 加载要写入的数据
rcall spi_transfer ; 发送数据字节
sbi PORTB, SRAM_CS ; 拉高SRAM片选(取消选中)
ldi r16, 5 ; 设置延时5ms
rcall delay_ms ; 调用延时子程序
ret ; 返回
; SRAM读字节子程序
; 功能:从SRAM指定地址读取一个字节数据
; 输入:addr_high, addr_mid, addr_low - 24位地址
; 输出:read_data - 读取到的数据
sram_read_byte:
cbi PORTB, SRAM_CS ; 拉低SRAM片选(选中芯片)
ldi r16, CMD_READ ; 加载读命令
rcall spi_transfer ; 发送读命令
lds r16, addr_high ; 加载地址高字节
rcall spi_transfer ; 发送地址高字节
lds r16, addr_mid ; 加载地址中字节
rcall spi_transfer ; 发送地址中字节
lds r16, addr_low ; 加载地址低字节
rcall spi_transfer ; 发送地址低字节
ldi r16, 0x00 ; 加载哑元数据用于接收
rcall spi_transfer ; 发送哑元数据并接收SRAM数据
sts read_data, r16 ; 保存读取到的数据
sbi PORTB, SRAM_CS ; 拉高SRAM片选(取消选中)
ret ; 返回
; =============================================
; 延时子程序
; =功能:提供精确的微秒和毫秒级延时
; =============================================
; 毫秒延时子程序
; 输入:r16 - 延时的毫秒数
; 使用寄存器:r16, r17, r18
delay_ms:
push r17 ; 保存r17寄存器
push r18 ; 保存r18寄存器
delay_ms_outer:
ldi r17, 200 ; 设置外层循环计数器(200次)
delay_ms_inner:
ldi r18, 250 ; 设置内层循环计数器(250次)
delay_ms_inner2:
dec r18 ; 内层循环递减
brne delay_ms_inner2 ; 内层循环未结束则继续
dec r17 ; 外层循环递减
brne delay_ms_inner ; 外层循环未结束则继续
dec r16 ; 毫秒计数器递减
brne delay_ms_outer ; 未达到指定毫秒数则继续
pop r18 ; 恢复r18寄存器
pop r17 ; 恢复r17寄存器
ret ; 返回
; 微秒延时子程序
; 输入:r16 - 延时的微秒数
; 注意:基于1MHz时钟,每个循环约4个时钟周期=4微秒
; 实际延时时间 = (r16 * 4) 微秒
delay_us:
nop ; 空操作(1周期)
nop ; 空操作(1周期)
nop ; 空操作(1周期)
dec r16 ; 计数器递减(1周期)
brne delay_us ; 不为零则跳转(2周期为零跳转/1周期继续)
ret ; 返回
; =============================================
; 程序结束
; =============================================
|