找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2906|回复: 0
收起左侧

单片机程序如何做稳定的经验1

[复制链接]
ID:44609 发表于 2020-12-16 13:47 | 显示全部楼层 |阅读模式
1.中断和 main函数或其子函数 里不要同时有同一外设模块信号的操作,如对74hc595的操作,对HT1621B的操作的信号线必须是有序的,而不能穿插,否则容易错乱或花屏
2.中断里标志位变化时,回main函数或其子函数后尽量再判断标记位,否则容易导致遗传上次标志位的作用,而没有及时发现中断返回后标志位已改变,而导致程序混乱
3.类似于信号量临界区的稳定性处理
4.对所有死等待如等待某标志位 ,做超时退出处理,防止卡死
5.所有数组下标尽量在访问数组前都做下标是否越界判断
5b.做除零异常判断,如果分母为零就不除
5c.注意:如果程序里有很多个定时器或多个高速外部中断在工作,而且定时频率都很高,那么如果栈空间分配很小,那么很容易发生嵌套的多层次抢占中断而在进入中断函数时发生局部变量分配储存空间时失败即堆栈溢出,而导致死机和卡死!,尽量
把功能用分支if做到1个定时器中断里,少用定时器,并尽可能减小定时器频率,并尽可能加大栈初始化空间,如stm32单片机里 .s 文件里的一些定义
6.对于一些潜在的不稳定因素(甚至1-2个月才出现一次故障),比如网络忙时包ID的先后次序发生变异,而导致if xxxx.id>old.id 永远无法再成立而卡死
7.多机通信尽量用单向通信,比如主从模式,主机发包轮询查询并从机返回结果模式,双向的全双工通信模式的稳定性比较差
8.所有按钮处理必须做按钮去抖动算法,甚至不是阻塞式延时式的去抖动算法,而是状态机时的异步流程方式的过滤掉按得过快的2次按钮事件间隔,比如200ms内再无按钮事件且按钮已没按住才算按了1次,而且最好加个104电容在按钮里并联
9.多机主从通信尽量用 目的机ID+通信协议版本号+分包的子包ID序号(如1-10)+子包总数+本子包数据长度+上次包的全局ID+crc32校验,方式来处理重复包,30ms内没收到从机反馈数据则重发包(比如重发4-6次)来处理丢包,并当上次包的全局ID大于一定值时发清零包ID的cmd包来复位ID计数,但要判断从机先收到大点的ID的包的情况
10.网口通信比usb通信稳定,usb在干扰时容易掉线
11.TF卡只适合做只读的fat32文件系统应用,如要常写入,最好配合 W25q64来固定分片方式的无fat32文件系统式的写入,因为TF卡常写入,容易写坏文件系统格式,比如意外掉电时等
12.对于一些串口接口的模块,如果波特率用得太大,则单片机容易串口中断响应不过来,而发生嵌套的异常串口中断,或丢串口数据,导致通信不稳定,波特率最好低点!
13.单片机程序的架构最好做成异步式的状态机式的虚拟多线程方式,不要用任何阻塞式的死等待和延时,延时也要做成状态机里的某个状态的delay的异步非阻塞式的累加,加到一定值才进入下1个状态码,异步方式比阻塞式的方式的稳定性要好,处理实时性也高,多级菜单的屏幕显示和按键处理也可以用状态机式的异步流程,有个状态码指示当前菜单的第1级选择项号,选择深度,第2级选择项号。。。。
14.相同的类似的属于同一功能概念下的处理过程最好集成到一个函数里做分支判断,而不要把1个功能分散到很多个函数里,这样代码比较难维护和二次修改
15.可以抽象为1个功能或1个流程体内的东西最好归纳法和抽象法抽象一下,不要做成if分支遍历和穷举所有情况方式,就像要做成矢量图,而不是像素图,抽象概括后的代码写法更有简洁性,更
有可维护性和稳定性!
15b.复杂度尽量做成可配置数组里的数据,用配置数据来展开对代码的控制,而不是把复杂度全部映射到代码if分支等分支上,就像要做成矢量图,而不是像素图,就像一个图形圆在可配置数组里(矢量图里)是 yuan_type和sin cos,而若是代码分支法,则是一堆关于圆的点阵

15c.按15点所说的抽象和归纳代码后的代码简洁化和可维护性加大化,可以使代码的条理和逻辑层次更加清晰,使更容易分析和预感到可能的bug和细节逻辑不严谨,从而预防bug和不稳定性bug,最终影响程序运行时稳定性,所以bug是由思维决定的


16.看门狗在程序完工后最好加上去,软件里的关键数据最好做RAM区多处备份,如备份10份,死机自动复位恢复后,检查RAM区里备份里的各备份,重复次数最高的那几份备份(且重复次数在6次以上)最可能为原始未被破坏的数据,并做个ram标志位检查是上电复位,还是看门狗复位,复位后可以做不清除ram数据,程序跑飞后,可以提前做些标志位判断,如跑飞到某处,可能某标志位未被置位却执行到这,则可以自动判断出是已跑飞,则自动软件复位一次!

17.
单片机芯片的最近的外围一圈上的单片机芯片vdd,gnd引脚附近必须多弄几个0603封装的贴片104电容来去干扰,pcb电源类走线的线宽必须要宽,至少0.4mm以上,甚至0.8mm以上,单片机芯片在pcb局部时尽量靠近电源芯片和供电芯片处

就单单才零点几欧电阻的导线稍微长点来传输电源,比如20cm长,就会导致高频杂波变多,所以导线末端务必加470uf以上的电解电容来去耦滤波,可以想象,在单片机引脚周围1圈上也应该最好加上几个470uf电容和104电容环绕
;大功率pcb供电线走线千万不要在单片机周围走线或走过单片机,一定要把大功率电源的输入接口做在大功率电路部分的附近,避免电源线的长距离走线!,要走也不要在单片机外周走过!,且大功率pcb供电线走线要边走线边1路隔个几段距离就加个104去耦电容



18.
如果是软件模拟spi信号接口或i2c接口,那么字节发送函数里最好先禁止总中断使能,发完后再开启总中断使能,否则容易不稳定

19.
c8051f340单片机芯片工作时如果电源电压比较低或有突然的电源脉冲浪涌干扰,容易使烧好的程序丢掉,稳定性好像不怎么好,stm32单片机芯片或GD32芯片好像就好点,就没这个问题!,推荐优先选后者

20.
对于以太网网络模块芯片应用,最好把这芯片的RST复位信号接到单片机上,并RST脚接个104电容(有时候干扰容易导致意外硬式复位,而反而不稳定),这样,网络卡死时可以强制复位网卡芯片一次来修复

21.
lwip等网络协议栈的某些版本有可能有内存泄露bug,或长时间运行后卡死,最好做定时自动软件复位单片机功能,来防止内存泄露过度

22.
芯片上如果flash够,最好做从服务器查询新版BIN固件来自动升级单片机自身的hex固件功能(固件下载完要做CRC32校对),以及usb的固件升级功能,即net bootloader和usb bootloader,来应对【做项目即使完成和验收后,几个月出现的bug要修复,而措手不及】


23.
比如 休眠后唤醒时给PB1引脚1个高电平后不明原因的死机,不一定是进入未定义中断,而是main函数之前bootloader对PB1响应:主程序进入休眠态,唤醒后 pb1为高,进入bootloader态,但bootloader里没有重新初始化stm32 cpu的时钟系统等,故卡死,虽然bootloader后跳转到的main里有重新初始化stm32 cpu的时钟系统等的代码,但是已执行不到


24.
密脚芯片单片机等ic焊接时上松香吸锡后,如果用酒精清洗,是否过几个月后会发生引脚间松香,助焊剂等物质发生变质而短路?而不稳定?

25.
故障实时监测功能很重要!(备灾1):比如某stm32版的带网口的板子做个板子是否在线监测和掉线时短信提醒功能,即每10秒钟发送1个心跳包给服务器,如果服务器50秒内没收到某板子的任何心跳包,则此板子判为已掉线,并发送报警短信,email给对应的技术员来通知处理和修复

26.
同一个安装点上弄多个冗余板子,某块故障时,另1块自动切换到上线!,或双电源,1个后备电池逆变电源,主电故障则自动切换到备电(备灾2)

27.
电源接插头不要用圆孔的那种电源插座或usb接口插座,要用专用的接线端子母座和接线端子插头(绿色方形的带2针的那种,最好还带上扣机制,插紧后自动上扣),否则电源不稳或接触不良,导致板子掉程序或其他异常,信号线也可以如此处理


28.
回流焊最后1段温度曲线的达到最高温度时如保持时间过长(推荐205度保持15秒,对于无铅锡膏)导致锡膏被烤干,或锡膏过期了,就会导致出来后焊接效果不好,比如焊盘上锡的亮度不高,无光泽,太干燥,这时容易影响产品性能和加速日后的产品性能衰变

29.
eeprom写入前不需要擦除整个扇区,比如at24c02之类的,而单片机自带的flash和w25q64等都是flash,写入前如果不擦除整个扇区,可能部分字节会写错字节,而导致异常和不稳,或者做了擦除处理,但是写某个数据时
未备份扇区里原数据而直接擦除扇区,导致数据丢失,比如要写入的数据数组跨越了2个扇区的边界的情况

30.
尽量不要用一些外置的什么is62lv256+ls373方案的扩展ram内存方式,而要直接用自带高ram大小的192K ram的单片机,比如stm32f407或stm32f103zet,stm32103re ,gd32f103cb等,因为外扩方式容易不稳定,比如受焊接质量,打板精度,环境湿度温度,电源波动等影响
而不稳


31.
递归函数尽量用while/for循环+栈或数组或链表的方式来替代,而不要做成多层次调用函数自身方式的递归,做也做成带递归深度变量的,限制最大递归深度,否则容易堆栈溢出而死机

32.
当要进行可能的后果极其危险或非常重要,不能被任何干扰误触发的控制操作时(比如房屋爆破队驱动炸药点火的装置,自动驾驶的紧急刹车制动驱动器,高压总闸开关继电器),务必要备份10份以上的
控制码,如果只有1份控制码,当这份控制码被强干扰信号改变内存数据而变为导致触发的目标控制码时,就会误动作,而如果是10份,会校验所有10份控制完全一致后再打开控制,这样只有1份或2份被干扰
后就不会误动作了,所有内存区的任何变量也都实时备份10份以上,这样被干扰后可实时恢复正确的数据(残留下的重复次数最多的那几份且重复次数在6次以上即为未破坏数据),
当10份备份中重复次数在6次以下,意外着系统也无法自己自我修复,就会自动进入崩溃状态或自动软式复位,
再进行重要控制或驱动前,也检查所有内存区的各变量的实时备份的10份数据,如果任何一个变量的10份备份中各数据间重复次数在6次以下,则意外着干扰太强了,系统已错乱,则不继续进行重要控制或进行驱动。
,进行i/o控制的代码最好在前面多加几百行陷井代码和判断是否已被干扰弄崩溃的代码,来尽可能减少意外直接跑飞到【进行i/o控制的代码】的概率,或者不用任何简单的i/o来控制重要设备或高压的重要设备,
而用i2c或spi接口的外围驱动芯片来间接控制,且i2c或spi必须为软件模拟,在i2c或spi的发字节函数里,每发1个比特就再判断一次【是否已被干扰弄崩溃即检查所有变量的10份备份是否还算完整和一致】,
这样就非常可靠和稳定,不会被强干扰弄误触发了!


33.
在存在强电流,强功率,强电压,强静电,强射频耦合传递,或可能有接触式火花的地方尽量多的加光耦隔离来隔离信号控制计算处理部分和高压信号驱动部分,如果一个系统上存在2个或以上独立供电电源,
相互之间也必须完全隔离(GND等都不要接一起),用光耦耦合来传递控制,
继电器尽量换为可关闭的晶闸管,或场效应管,或其他大功率开关管,这些控制开断时有【无火花】的优点,防止继电器通断时的火花放电间隙导致的强干扰,如果实在要用继电器,在继电器的2触点引出脚间接入RC去火花电路,
,并在继电器的2触点引出脚控制的真空电磁阀或电机等感应负载上反向并联入【1个高耐压二极管+串1个0.5欧左右的大功率电阻】,以及并联入1个体积大的104电容,来去除真空电磁阀或电机等在断电瞬间产生的
反向电动势,从而消除反向电动势在继电器触点刚断开时的微小间隙里产生火花,从而消除火花产生的强干扰来使单片机跑飞,死机或复位的后果,


,另外如果用继电器,最好每个继电器的VCC串1个10欧的电阻后再控制继电器,这样继电器吸合时对电源的脉冲干扰小点!


34.
做编程项目或电子项目,务必做一个 发现的未解决的程序bug的备忘.txt ,及时记录发现的偶尔出现的bug,和突然灵感里闪现的预感到的可能会出现的bug情况,以及要改进的地方,
比如一些操作不变鸡肋问题,稳定性问题,都要及时记录,因为如果每忘记1点点bug,都意味着可能日后会出现这个bug,即导致项目的彻底失败,毕竟【一个项目的成功是靠堵住所有一些可能的细节虫洞和鸡肋来做到的】

35.
如果是在别人的开放平台或接口上做程序,那么需要定期检查所有情况都是否正常,比如即使今天全部正常,但是他们平台也许早就换了接口版本,只是老用户还是维持老版本接口,新订购的用户用我们isv软件时却已切换到新接口,不能用,却发现不了

36.
字符串数组即二维的uchar数组,在定义时,前1个方括号索引字符串序号,后1个方括号索引当前字符串的字符下标,比如u8 wavfile1n[7][15] 代表7个最长14个字符的字符串,方括号别弄反了,容易异常


37.
板子上电源处加的电解电容在1000uf以上时,容易上电时电容瞬间电流过大而导致开关电源发生短路保护,如此循环,而不易启动


38.
EMI:pcb电源输入部分增加共轭电感器抑制中高频的共模噪声,并加共模滤波电容(2个电容串联,2端接电源正负,中间点接地)和差模滤波电容

39.
如果要抗雷击浪涌,应在电源输入处并联入动作电压大于额定电压2-3倍以上的压敏电阻,并且电源输入处pcb走线宽度要粗,并电源输入处的压敏电阻前的走线串联入玻璃保险管的座的相关元件

40.
pcb信号线的走线尽量短而且宽: pcb布线时选择合理的导线宽度 由于瞬变电流在印制线条上所产生的冲击干扰主要是由印制导线的电感成分造成的,因此应尽量减小印制导线的电感量。印制导线的电感量与其长度成正比,与其宽度成反比
晶振走线必须尽量短:           时钟线、信号线也尽可能靠近地线,并且走线不要过长,以减小回路的环面积
信号线走线拐角应采用圆弧形:   电路板上的印制线宽度不要突变,拐角应采用圆弧形,不要直角或尖角。(9)时钟线、信号线也尽可能靠近地线,并且走线不要过长,以减小回路的环面积。
保持环路面积最小,降低干扰对系统的影响,提高系统的抗干扰性能。并联的导线紧紧放在一起,使用一条粗导线进行连接,信号线紧挨地平面布线可以降低干扰。电源与地之间增加高频滤波电容
采用完整的地平面设计,采用多层板设计,铺设地层,便于干扰信号泄放。

41.
密脚芯片怀疑存在连焊时的处理技巧1:拖焊时务必放很多松香到吸锡铜丝线上,否则容易吸锡不彻底,而留下ic引脚间不稳定性的短路问题,最后用99%浓度(不能含水多)的酒精擦去残留的松香,并再烙铁顺引脚刮下来清理一遍酒精等


42.
尽量用全局变量或全局数组变量替代局部变量,因为进入函数时要不断的分配局部变量,容易影响执行效率和耗用完堆栈空间等;

43.
电源部分的正常和稳定(比如电压波纹小,高频噪声滤除率高,功率实足,抗突变的浪涌大电流能力强)是单片机电路和控制电路稳定工作的最重要的前

44.
像LM1086-3.3或LT1086-3.3,LMXXXXX....之类的贴片式三端稳压器,在贴片工艺完后并过回流焊炉后,由于焊盘上印刷锡膏时锡膏量不足或者三端稳压器引脚上翘会导致三端稳压器过回流焊炉焊接后存在虚焊或接触不良,
所以应该每次都手工补焊下来补点锡,否则容易不稳定,或在电子产品运输工程中脱焊而开路
sot23封装的贴片三级管也容易引脚上翘而导致回流焊的焊接质量不良而导致开路或虚焊(如在运输过程中振动而脱焊)

45.
所以出厂前的24h老化测试和pcb带电工作的高强度振动测试也很重要,来检测是否有虚焊的现象(如在运输过程中振动而脱焊)

46.
电源开关等不要用机械式的单刀双掷式的那种微型拨动开关,很容易损坏或接触不良,最好做成自锁式的那种按钮式电源开关或船型开关


47.
注意场效应管的前置驱动电压放大级用的三极管的发射极最好是接地形式的而场效应管G端接三极管的集电极再1个上拉电阻上拉到比如24V,否则如果NPN的发射极接G端时,当三极管导通,
发射极的电压最大也只有基极的电压的大小,将无法良好的使场效应管完全导通,比如IRF540N的Vgs需要10V左右才完全导通,另外应该在G端对地并联1个10V稳压管,因为一般Vgs也不能太大,20V左右最大了

不要用单片机IO口直接驱动场效应管,因为一般场效应管Vgs在10V左右才能完全导通和饱和,单片机io口电压不够,会驱动不良

48.
光耦存在寿命,所以尽量用非光耦的取代元件来做隔离,比如一些ic:
光耦中的发光二极管随着时间推移而老化;比如开关电源反馈环路中光耦老化,传输比下降后可能引发故障。
不能用线性光耦作为模拟量传感:通光耦产品手册中对电流传输比只给出一个大概的范围(从低到高可以差别一倍),而且没有给出温漂和老化的指标。用于开环传输模拟量,精度是得不到保证的,所以导致过压、过流,以致损坏

49.
74hc595的时钟和数据线上要加阻容滤波的  这类IC  速度太高   对毛刺干扰特别敏感,而导致误动作或屏幕错乱

49a.
做电压检测电路时,比如测220V电压或380V电压,不要用线性光耦+1M电阻来降压采样,而要用微型工频变压器(铁芯那种(非高频磁芯))来降压并整流采样,因为光耦会老化和被击穿(雷击等意外),或有温飘

50.
不要在任何中断函数里调用1个需要等待另1种中断函数来置位目标所需标志位才能结束的函数或代码段,否则可能因为中断优先级的关系而导致这另1种中断函数永远无法执行而卡死在等待里,导致死机

51.
谨慎使用keil自带的库函数之sprintf和printf,容易出问题,比如卡死或内存泄露

52.
LQFP64/LQFP48之类的密脚IC如果用拖焊法手动焊接,那么如果芯片在pcb上的每个引脚的焊盘的长度如果比较长,则很容易拖焊后在引脚的焊盘的靠芯片内部方向处产生锡的残留而导致2个引脚焊盘间的短路,所以
此法不稳定,还是用钢网刷锡膏+回流焊法比较可靠!




53.
ad采样均值滤波法时应该用:

               
                        chindex=0;
                        
                        
                        
                        
                        
                        
                          memcpy(&ch_vout1_lvbo_temp[0],&ch_vout1_lvbo[chindex][1],(junzhi_lvbo_count-1)*sizeof(float));
                        
                                memcpy(&ch_vout1_lvbo[chindex][0],&ch_vout1_lvbo_temp[0],(junzhi_lvbo_count-1)*sizeof(float));
                        
                        
                        ch_vout1_lvbo[chindex][(junzhi_lvbo_count-1)]=read_adc_value();
                        
                        
                if(ch_caiyang_alled[chindex]==0){
               
                        ch_vout1_lvbo_index1[chindex]++;
                        if(ch_vout1_lvbo_index1[chindex]>=(junzhi_lvbo_count)){
                                
                                ch_caiyang_alled[chindex]=1;
                        }
                        
                }
        

这样的代码机制,而不是插满一次缓冲池后从头开始覆盖,而是不断往前移1格来不断的从缓冲池的末尾插入,而且memcpy时注意是(junzhi_lvbo_count-1)*sizeof(float)个字节,而不是(junzhi_lvbo_count-1)个


54.
数字电源或开关电源的mos管的驱动级的信号输入最好带一级最大高电平时间限制的硬件式电路,比如最大允许高电平500us,来防止卡死时一直高电平而使mos一直导通而烧坏mos或高频变压器!


55.
注意临界大小判断时容易出现的bug:
        if((ch_vout1_zhankongbi_value1+diff_v1)<=max_zhankongbi1){
         ch_vout1_zhankongbi_value1=ch_vout1_zhankongbi_value1+diff_v1;               
        }

,这里,如果diff_v1很大,但是ch_vout1_zhankongbi_value1+diff_v1刚好只比max_zhankongbi1大1,那么就会陷入死循环,永远无法达到max_zhankongbi1,而且不是只离max_zhankongbi1一点,容易使调节环在误差比较大时卡死
,所以要加else来改为:
        if((ch_vout1_zhankongbi_value1+diff_v1)<=max_zhankongbi1){
                ch_vout1_zhankongbi_value1=ch_vout1_zhankongbi_value1+diff_v1;               
                 }else{
                ch_vout1_zhankongbi_value1=max_zhankongbi1;
                                                         
                 }


56.
数字电源的每次反馈调整pwn或每次pid调整时的调整间隔周期必须在相隔500us左右以上而不是1个while死循环里不停的调整,
而要用timer定时定间隔的调(所以输出级的电解电容务必2200uF以上,来延长电压突变缓冲时间),而且每次调整的误差比例乘积因子必须要小,否则容易输出电压波纹很大!
控制输出电压波纹的关键是这500us内不断的采样adc(可多次DMA式adc)来累加并在500us结束时取累加值的平均值来数字滤波,因为一般ad采样电压的波纹比较大


57.
keil中 3.11/6.1会被当做小数来计算结果,如果是3/23,则结果为0,因为被当做整数来处理,所以必须写为(float)3.0/(float)23.0

58.
如果需要动态改变单片机定时器的定时周期,那么要注意周期定时值不能被改的太小,否则会导致不断的高速的进入定时中断而卡死,要判断和限制下


59.
【bug调试和发现bug原因,解决问题现象】的原则是【在相对系中寻找局部绝对系来作为支撑点,进而慢慢全部覆盖问题机制】,比如找几个确定点和可以差量法,对比法确定的程序现象和机理,然后再慢慢消元,减少变化量的个数,尽量避免多个量
一起在变的问题框架,因为变量1多,要测试的样本就成指数级增长!


60.
c#上位机程序防卡顿经验1:
阻塞式的任务和代码要全部在后台线程里独立运行,而不能在主界面的主线程里执行,对于后台线程里要跨线程操作主界面控件的,这段操作界面的代码要用到Invoke,相当于卡死界面一点时间,所以在
Invoke里调用的 Action<string> actionDelegate3 = (x) =>{。。。。}之类的里的代码尽量执行时间耗时极短,比如尽量把界面控件某状态的死等待代码不要放在Invoke的执行体代码里,
比如 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }这段等待webbrowers加载网页完毕的代码不要放在Invoke的执行体代码里,而放在
线程代码里,即Invoke的执行体代码执行完后的紧接着的下一句代码再执行 while ((int)(webb1.Tag) == 0 || webb1.IsBusy == true) { System.Threading.Thread.Sleep(10); }
,而Invoke的执行体代码里如果执行webbrowers1.Navigate(aaa1.url);之类的,也会在打开网页时卡顿一段时间界面,所以尽量改为异步方式的Navigate函数,比如BeginNavigate()之类的

61.
C#里等待webbrowser是否加载网页完成的稳定可靠的检测方法为:延时式再判断累计法(注意:线程里读webbrowser的Isbusy值时要用Invoke,否则容易出错):

                      int kongxianed = 0;
                      while (true)
                      {

                          System.Threading.Thread.Sleep(100);
                          bool isbusy1=true;


                          Action<string> actionDelegate3a11 = (x) =>
                         {
                             isbusy1 = webb1.IsBusy;
                         };

                          webb1.Invoke(actionDelegate3a11, "");


                          if (isbusy1 != true)
                          {
                              kongxianed++;

                          }
                          else
                          {
                              kongxianed = 0;

                          }

                          if (kongxianed > 20)//如果2秒内都无Busy状态再出现,那么极有可能网页和其所有网页子框架都已加载完成
                          {
                              break;

                          }


                      }


62.
c#里容易异常的地方和一些界面控制代码外围尽量包1层try ...catch,来防止崩溃出错

63.
c#里object 不能直接隐式转为int,而要先tostring()后再int.parse?


64.
js里的ajax里提交后尽量加&r="+Math.random()后缀来防止缓冲而不更新

65.
路径可能变动的网页里的带背景img定义的css尽量不要放在网页里,而放在一个外部css文件里,这样图片可以相对css文件来定义路径

66.
对于ajax点击链接执行函数但不跳转,建议不要用href:void(0)...+onclick(兼容性不好,有些浏览器异常打开新窗口),而用div+onclick+div加手型鼠标css
,而且onclick所在div要显式定义好height和width,否则容易兼容性不好,而有的浏览器点击无响应!
Example:CSS鼠标手型效果 <a href="#" style="cursor:hand">CSS鼠标手型效果</a>
Example:CSS鼠标手型效果 <a href="#" style="cursor:pointer">CSS鼠标手型效果</a>
注:pointer也是小手鼠标,建

另外免费招单片机徒弟一起学习交流,看我头像
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表