本帖最后由 taotie 于 2020-8-22 10:17 编辑
这是一个外国网站的程序,在bascom avr平台采用basic语言与汇编语言混合编程。下载回来对其进行部分改造:
去掉lcd16x4液晶改用流行的oled屏仿真。
增加中文设置提示。
注意:源程序是用Bascom-Avr-1.11.9.1编写的,在新版Bascom-Avr须在非汇编块中的汇编指令前增加“!”字符
$regfile = "m8def.dat"
$framesize = 32
$swstack = 32
$hwstack = 32
$crystal = 8000000
Config Portb.1 = Output '一个引脚配置为LED输出
'********* LCD ***************************************************
''设置LCD模块的针脚,必要时根据您自己的连接
' Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0
'Config Lcdmode = Port
'Config Lcdbus = 4 '4 bit mode
' Config Lcd = 20 * 4
' Initlcd
'Cursor Off
Config Scl = Portc.5 ' 用I2C引脚Scl = Portc.5 ,Sda = Portc.4
Config Sda = Portc.4
Config Twi = 400000 ' i2c 的速度
I2cinit
$lib "i2c_twi.lbx" ' 不使用模拟I2c的软件,而是使用twi
$lib "glcdSSD1306-I2C.lib" ' 用glcdSSD1306-I2C库替换默认库要添加在bascom avr库中
#if _build < 20784
Dim ___lcdrow As Byte , ___lcdcol As Byte ' 老版本变量格式进行编译
#endif
Config Graphlcd = Custom , Cols = 128 , Rows = 64 , Lcdname = "SSD1306" '配置图形显示器
'********* ISR Timer 2 ***************************************************
Config Clock = User '配置用于Time$和Date$变量的计时器。
'您可以以128Hz的频率去抖动(最大延迟4/128 = 0.031s)
Config Timer2 = Timer , Async = On , Prescale = 1 '预分频器8->16Hz或1->128Hz
On Ovf2 _isr_t2ovf Nosave
Enable Ovf2
Enable Interrupts
Config Date = ymd , Separator = - '配置日期格式 欧洲:dmy 美国:mdy 亚洲:ymd
_sec = 00 : _min = 15 : _hour = 14 : _day = 21 : _month = 08 : _year = 20
Dim Flagrtc_changed As Bit
Dim Tick_128hz As Byte
'********* 输入键和去抖***************************************************
'将按钮分配给引脚-它们必须全部连接到同一端口!
Const Key_plus = &B0000_1000 '1 =按钮输入
Const Key_minus = &B0001_0000
Const Key_enter = &B0010_0000
Const Pinb_mask = Key_plus + Key_minus + Key_enter '过滤输入的掩码
Const Pinb_mask_compl = &HFF - Pinb_mask 'Pinb_mask compliment
'这是PortB上的按钮
Ddrb = Ddrb And Pinb_mask_compl 'DDRB: 0=INPUT 1=OUTPUT
Portb = Portb Or Pinb_mask '1 =激活输入的内部上拉电阻
Key_port Alias Pinb
Dim Key_state As Byte '0/1按钮的反转状态(1 =按下)
Dim Key_press As Byte '触发按键
Dim Key_rep As Byte '自动重复计数器
Dim Key_ct0 As Byte , Key_ct1 As Byte '2 位计数器用于反跳
Const Key_repeat_start = 127 '计时器ISR的编号-1,直到自动重复开始(大约500-1000ms)
Const Key_repeat_next = 15 '定时器ISR的编号-1,用于自动重复重复率(大约125毫秒)
'反跳初始值
Key_ct0 = 255 '拦截程序从按键开始
Key_ct1 = 255
'Key_rep = Key_repeat_start 'Paranoia
'********* 一般的 ***************************************************
Dim I As Byte , J As Byte , K As Byte , L As Byte
Dim q As Byte
Dim Setdatetime_index As Byte
Setdatetime_index = 2 '日期数组的第一个索引为1,2 = _min
Dim Date_array(6) As Byte At _sec Overlay '秒/分钟/小时/天/月/年的数组指定在内存中覆盖变量。
'********* 主循环 ***************************************************
Do
' Home '光标原点
' Lcd Date$ ; " " ; Time$ '显示日期和时间
' Locate 2 , 1
' Lcd "Set: " ; Lookupstr(setdatetime_index , Data_dateset) 'var = LOOKUPSTR(索引,标签)从表中返回一个字符串
Setfont font8x8
Lcdat 2, 16 , Date$
Setfont font12x16
Lcdat 4, 16 ,Time$
Setfont font8x8
'Lcdat 7, 16 , "Set: " ; Lookupstr(setdatetime_index , Data_dateset) 'LOOKUPSTR(索引,标签)从表中返回一个字符串
'为了节省电源,在ASM中编写了128Hz环路:
Setfont font12x16
q=Lookup(setdatetime_index , Data_set)
Lcdat 7, 16 ,chr(134);chr(135); ": " ; chr(q)
Flagrtc_changed = 0
Enable Interrupts 'Paranoia
'进行节电循环直到((Flagrtc_changed = 1)或(Key_press> 0))
Main_1:
Config Powermode = Powersave '配置电源模式=省电模式 如果定时器/计数器2启用,它将在睡眠期间保持运行
'省电模式仅适用于8535、Mega8、Mega163。
! lds r16,{Key_press} '按下按钮了吗?
! TST r16
! BRNE main_2
! lds r16,{flagRTC_Changed} '再来一秒钟?
! sbrS r16,bit.flagRTC_Changed
! RJMP Main_1
Main_2:
If Key_press > 0 Then Gosub Setdatetime_input
Loop
'---------------------------------------------------------------
'子例程:Setdatetime_input 设置日期时间输入
'呼叫来源:main
'目的:设定日期和时钟
'参数:键输入
'---------------------------------------------------------------
Setdatetime_input:
Disable Interrupts '禁用中断
I = Key_press '读取键
Key_press = 0
L = Lookup(setdatetime_index , Date_max)
If Setdatetime_index = 4 Then
L = Lookup(_month , Date_month)
K = _year And 3
If _month = 2 Then If K = 0 Then Incr L
End If
K = Lookup(setdatetime_index , Date_min)
J = Date_array(setdatetime_index)
If I = Key_plus Then
If J < L Then Incr J Else J = K
End If
If I = Key_minus Then
If J > K Then Decr J Else J = L
End If
Date_array(setdatetime_index) = J
Enable Interrupts '启用中断
If I = Key_enter Then
If Setdatetime_index < 6 Then Incr Setdatetime_index Else Setdatetime_index = 1
End If
Return
'********ISR计时器2***************************************************
'---------------------------------------------------------------
'子程序:'u isr_t2ovf
'呼叫来源:定时器2(128Hz)
'目的:RTC,去抖动
'结果:RTC:_秒、_min、\u小时、\u天、\u月、\u年
' '反跳:请参见下文
'---------------------------------------------------------------
$external _soft_clock
Const _sectic = 0 'CONFIG CLOCK = USER,GOSUB <> SECTIC的编译器语句
_isr_t2ovf:
$asm
push r16
in r16,sreg
push r16 ' 将R16和SREG保存在堆栈中
' 防抖键
push r17 '现在保存所有使用的寄存器
push r18
PUSH r19
Call Debounce_port
POP r19 '恢复寄存器
pop r18
pop r17
lds r16,{tick_128hz} '每1/128秒增加一次tick_128
inc r16
sts {tick_128hz},r16
andi r16,128-1 '如果_tick128不是128的倍数
brne _T2OVF_END ' set_digit退出isr,否则已过去一秒钟
' 该位变量将每隔一秒钟设置一次
lds r16,{flagRTC_Changed} '位变量
sbr r16,2^bit.flagRTC_Changed
sts {flagRTC_Changed},r16 '注意标志是重置标志的主要代码责任
' 转至内部Bascom ISR例行程序:软时钟
pop r16 '获取SREG的内容
!out sreg,r16 '恢复sreg
pop r16
JMP _SOFT_CLOCK '原始RETI
_t2ovf_end:
pop r16 '获取SREG的内容
!out sreg,r16 '恢复SREG
pop r16
$end Asm
Return '带着RETI离开isr
'*********防抖******************************************* ************
'------------------------------------------------- --------------
'子程序:Debounce_port
''同时检测最大 8个按键,具有反跳和自动重复功能
'每个键被扫描3次以更改状态
'使用的寄存器:R16,R17,R18,R19
'变量:键0/1的Key_state状态
'Key_rep自动重复的重复计数器
'Key_ct0 / Key_ct1 2位计数器,用于反跳
'结果:Key_press:检测到按下键时,引脚位变为1
'读出后在Main中重置Key_press的状态!
'感谢彼得·丹纳格(Peter Dannegger)
'---------------------------------------------------------------
Debounce_port: '大约108个字节
$asm
in R18, key_port
com R18 '引脚低电平有效
ANDI R18, Pinb_mask '带Pinb_mask的过滤器引脚
LDS R19, {Key_state}
eor R18, R19 '在引脚输入处检测电平变化
LDS R16, {Key_ct0} '8通道2位计数器
LDS R17, {Key_ct1}
and R16, R18 '重置R17中的2位计数器:R16
and R17, R18
com R16 '递减R17:R16
eor R17, R16
STS {Key_ct0} , R16 '保存计数器
STS {Key_ct1} , R17
and R18, R16 'IF counter=&B11设置数字输入消隐
and R18, R17
eor R19, R18 '0<->1切换状态
STS {Key_state} , R19
LDS R16, {key_press}
and R18, R19
or R16, R18 '检测到“0->1按键
LDS R17, {Key_rep}
tst R19 'Key_state:是否按下任何键?
breq Key_isr_rep
dec R17
brpl Key_isr_finish 'IF Key_rep <0 set_digit等待时间到期
mov R16, R19 '自动重复key_press = key_state
ldi R17, key_repeat_next '设置自动回复重复计时器
rjmp Key_isr_finish
Key_isr_rep:
ldi R17, key_repeat_start '重置自动回复开始计时器
Key_isr_finish:
STS {Key_rep} , R17 '保存自动回复计时器
STS {key_press} , R16 '保存去抖键
$end Asm
Return
'********* RTC ***************************************************
'如果要使用以下空例程
'时间/日期bascom功能。 您可以根据需要填充它们
Getdatetime:
Return
Settime:
Return
Setdate:
Return
End
'******** 选择日期和时间 ************************************
Date_min:
Data 0 , 0 , 0 , 0 , 1 , 1 , 0 '‘秒”,“分钟”,“小时”,“日”,“月”,“年”最小标志
Date_max:
Data 0 , 59 , 59 , 23 , 31 , 12 , 99 '‘秒”,“分钟”,“小时”,“日”,“月”,“年”最大标志
Date_month:
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 '12个月月末标志
Data_dateset:
Data " " , "Sec " , "Min " , "Hour" , "Day " , "Mon " , "Year" '秒”,“分钟”,“小时”,“日”,“月”,“年”
Data_set:
Data 0 , 128, 129 , 130 , 131 , 132, 133 '秒”,“分钟”,“小时”,“日”,“月”,“年”
$include "../font8x8.font"
$include "../font12x16.font"
|