海思项目
1. 获取ATV预制频点的ini
文件数量接口
为了便于APK
移植,因此将接口核心逻辑做在底层,上层只传路径下去。遍历当前目录(目录由APK
上层通过参数传下去),通过检测文件名(预制频点文件格式是固定的)来判断ini
文件的数量。
dp = opendir("/atv/ini"); if (dp != NULL) { while (ep = readdir(dp)) { b = ep->d_name; if(b.find("programlist") != std::string::npos) flag1 = flag1 + 1; } (void) closedir(dp); }
Opendir : opendir()
用来打开参数name 指定的目录, 并返回DIR*形态的目录流DIR这一结构体,以下为DIR结构体的定义:
struct __dirstream { void *__fd; char *__data; int __entry_data; char *__ptr; int __entry_ptr; size_t __allocation; size_t __size; __libc_lock_define (, __lock) }; typedef struct __dirstream DIR;
函数
DIR *opendir(const char *pathname)
,即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用:
struct dirent *readdir(DIR *dp); void rewinddir(DIR *dp); int closedir(DIR *dp); long telldir(DIR *dp); void seekdir(DIR *dp,long loc);
对于获取文件名,我用到
readdir
、closedir
两个函数。dirent
不仅仅指向目录,还指向目录中的具体文件,readdir
函数同样也读取目录下的文件。以下为dirent
结构体的定义:
struct dirent { long d_ino; /* inode number 索引节点号 */ off_t d_off; /* offset to this dirent 在目录文件中的偏移 */ unsigned short d_reclen; /* length d_name 文件名长 */ unsigned char d_type; /* the type of d_name 文件类 */ char d_name [NAME_MAX+1]; /* file name }
通过d_name可以拿到文件名,用find函数判断是否文件格式对应。
if(b.find("programlist") != std::string::npos) flag1 = flag1 + 1;
注:
std::string::npos
是一个常数,它等于size_type
类型可以表示的最大值,用来表示一个不存在的位置,类型一般是std::container_type::size_type
。
2. 手动搜台更新的节目每次都是channellist
的第一个节目
经过分析,手动搜台结束后,会有通过HiManualScan
函数更新节目,而HiManualScan
函数中更新数据库(GetDBO()->Update
)时,channel_ID
是默认初始值,因此只需修改此处需要更新的节目ID即可,在update
之前先获取一下当前节目号(GetProgram
函数)即可解决问题。
3. 添加ATV--NTSC
制式的搜台方式包括盲扫功能
-
首先
NTSC
制式和正常的ATV
搜台方式区别在于,NTSC
的自动搜台并非和ATV
一样全频点搜台,NTSC
的Air
和Cable
有两个不同的频点表,通过频点表进行搜台。 -
盲扫功能解决方法:考虑盲扫和手动搜台功能相似,因此修改了手动搜台的接口底层逻辑,从上层
APK
传入channel_ID后,先通过频道表查到频点后进行正常搜台,完成后上层APK
停止搜台流程即可。区别于手动搜台的是,因为ATV全频点搜台参数下的频点,而盲扫参数频道号Air和Cable的范围不同,因此需要加异常处理,否则查表查不到时会把之前的数据清空。
-
在
NTSC
制式的导出频点、预制频点的时候,因为NTSC
的分表存储机制,在更新节目(更新数据库)接口里,更新Air和Cable有个位置区别。/* NTSC scan channel list number */ #define NTSC_SCAN_AIR_PROGRAM_MAX (69) #define NTSC_SCAN_CABLE_PROGRAM_MAX (135) HiDBOFact::GetDBO()->Update(HI_MW_ATTR_ATV_INFO,(HI_U8*)&stProgInfo, sizeof(HI_MW_ATV_PROG_S), i+NTSC_SCAN_AIR_PROGRAM_MAX);
4. 关于ini
文件牵扯到的iniparser
库的使用
iniparser
是一个C语言库,是针对INI
文件的开源解析器,iniparser
提供API
接口对ini
文件进行解析、配置、删除等操作。格式如下:
[wine] //组名必须用[]括起来 grape =cabernet Sauvignon year =2018 country =USA alcohol =12.5 //name 每个name对应的value值
iniparser基本API
dictionary * iniparser_load(const char * ininame); //加载ini文件,将数据存于dictionary结构中
const char * iniparser_getstring(const dictionary * d, const char * key, const char * def); //获取对应key的value。key以组+key的形式体现,如“`ipaddrpool:start`”。value以字符串形式返回,若未找到对应的key则返回
int iniparser_set(dictionary * ini, const char * entry, const char * val); //将dictionary中对应组的数据写入一个已打开的文件中。s是组名,f是文件流。
void iniparser_dump_ini(dictionary * d, FILE * f); //保存至文件
//项目中使用demo fp = fopen(inipath, "w"); ini = iniparser_load(inipath); iniparser_set(ini,ProgramNum, NULL); iniparser_set(ini, IdString,IdValue); iniparser_dump_ini(ini,fp); fclose(fp);
6. ATV的Channel lock
会出现画面异常无法遮黑&解锁画面遮黑大概率解除
-
问题1现象:节目lock后画面停止,输密密码菜单弹出,没有声音
初步分析:首先画面是锁定成功的,没有遮黑问题可能出在
freezeWin
时流程走的不对导致静帧参数错误导致。
问题现象是从锁定节目后复现的,锁台后锁定画面该部分的代码是在hi_mw_logic_lockmanager.cpp
、hi_mw_logic_atv_player.cpp
两个文件。考虑对比海思公版程序正常,对比上述两个文件的log信息,发现Lock_TotalLockEn
开关不一致导致节目画面mute的流程不一样,而这个变量是从db.ini
配置文件中读取的,因此打开Lock_TotalLockEn
配置后锁台正常
-
问题2现象:节目信号正常输入正确密码后画面没有解锁
初步分析:因为画面是黑屏并且没有声音,现象和静帧没有解的现象一样。通过
hidebug
串口进行手动解静帧画面正常,因此可以确定的是流程走错导致少解了一次静帧。
通过加打印信息发现,在hi_mw_logic_atv_player.cpp
中,OnSignalSupportSub
信号检测的函数中对解静帧有个判断:在童锁、channel
锁、bSrcLock
三个配置同时关闭时才会正常解静帧,但是channel锁不打开节目上锁又有问题。因此,在此处解静帧的条件出新加了一个判断该节目的属性lock的开关,如果两个值有任意一个为1,则都进行解静帧。
注:无信号的情况原因一致
7. DTV
搜台异常的几个问题总结
-
版型配置中demo、tuner型号选择错误
-
因为tuner灵敏度太低,导致个别高频点的台搜不到
-
代码中频偏过大,导致
NTSC
制式的最后一个频点(例如859.25)锁不住,原因是频偏值过大导致最后一个频点锁的是860,而节目在859.25就停止搜台了。
Python项目
1. 基于tkinter
比较筛选目录下是否存在相同图片
首先是先拿到路径下所有的图片,对比图片的相似度,经过百度有可以直接引用一个python的hash算法进行比较,传入图片路径和设定误差值即可。将相同的图片信息存在txt
文件中用来保存。
考虑到路径下可能存在多个文件夹嵌套、图片名字相同的情况,传入hash算法的文件名必须是带路径的,否则只能检测当前目录下的图片,并不能遍历到子文件夹中。
大概思路如下:
-
获取所有文件名(带路径),采用递归遍历获取所有带路径的文件名,将文件名和带路径的文件名存入字典中。开始考虑用列表存,但是在通过append函数循环添加时,参数是list时报错,所以就用的字典存放,后面查询也方便。
-
通过字符串切片用"."进行分割出后缀,即文件格式得到所有的图片
-
拿到所有图片并带路径,循环两两比较图片,调用比较函数
com_path = [""] def get_all(path): paths = os.listdir(path) # 列出指定路径下的所有目录和文件 for i in paths: com_path = os.path.join(path,i) if os.path.isdir(com_path): get_all(com_path) # 如果该路径是目录,则调用自身方法 elif os.path.isfile(com_path): if com_path.split(".")[-1] in ['jpg','png','bmp']: com_path_T = {com_path.split('\\')[-1]:com_path} compath.update(com_path_T)
注:hash算法比较图片函数(传入图片路径可直接使用)
def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0): ImageFile.LOAD_TRUNCATED_IMAGES = True hash_1 = None hash_2 = None image_file1 = open(image_file_name_1,'rb').read() Has = hashlib.md5(image_file1).hexdigest() image_file2 = open(image_file_name_2,'rb').read() Has1 = hashlib.md5(image_file2).hexdigest() if image_file1 != image_file2: return False with open(image_file_name_1, 'rb') as fp: hash_1 = imagehash.average_hash(Image.open(fp)) with open(image_file_name_2, 'rb') as fp: hash_2 = imagehash.average_hash(Image.open(fp)) #对比图片是否一样 dif = hash_1 - hash_2 if dif < 0: dif = -dif if dif <= max_dif: return True else: return False
函数compare_image_with_hash本身比较图片hash值的时候,发现对于一个图只改变了颜色,hash值是不变的,因此需要用
image_file2 = open(image_file_name_2,'rb').read()
Has1 = hashlib.md5(image_file2).hexdigest()
加以判断。
pyinstaller -F -w Test.py
命令进行打包
tkinter
基本用法
Root=tkinter.Tk() #创建Tk对象 Root.title("xxxxx") #设置窗口标题 Root.geometry("300x160+500+200") #设置窗口尺寸 XX=tkinter.Label(Root,text="XXXXXXXX") #标签 XX.pack() #指定包管理器放置组件
该方法没有对位置进行设置,可以用label.place(x=20, y=20)
进行位置设置。
线程简单用法
def thread_it(func, *args): '''将函数放入线程中执行''' # 创建线程 t = threading.Thread(target=func, args=args) # 守护线程 t.setDaemon(True) # 启动线程 t.start()
例如在按键启动线程执行函数:
tkinter.Button(Root,text="开始查找",command=lambda :thread_it(getuser(函数名),函数参数)).pack()
2. 基于tkinter
修改bin文件对应地址的值
首先bin文件的各个数据存在固定地址上,考虑用读取文件内容重新写入另一个文件中的方式,中间重新写入要修改的数据来实现修改文件的需求。
...... #define PANELINDEX_INFO_OFFSET 0x3000 #define TCONINDEX_INFO_OFFSET 0x4000 #define UART_SWITCH_OFFSET 0x5000 #define SOUNDPORT_INFO_OFFSET 0x6000 #define POWERON_MODE_OFFSET 0x10000 ......
修改文件的逻辑以及代码如下:
Re = open('deviceinfo.img','rb+') Re_t = open('deviceinfo_T.img','wb+') strb = Re.read(device_ddr[i]) Re_t.write(strb) Read_t = list(ReadData[i]) Read_t[1] = names['var'+str(i)].get() ReadData[i] = ''.join(Read_t) print(ReadData[i]) Re_t.write(bytes().fromhex(ReadData[i])) Re.seek(device_ddr[i]+device_size[i]) strb = Re.read() Re_t.write(strb) Re.close() Re_t.close()
-
read读到的数据类型为string不能直接修改,要先转为list后修改对应位置上数据后,再把list格式转为string格式
names['var'+str(i)].get()
获取的是用户输入的数据
''.join(Read_t)
将list格式的Read_t转为string格式
Re.seek(device_ddr[i]+device_size[i])
在写入后一部分数据时,文件指针需要移动到修改位置之后
RDA项目
1. 电视-电源设备串口通信加密/解密功能
项目简介
RDA方案512C
,基于串口通信的电源设备管理系统;客户要求电视的使用需要在电源设备的管理下进行,并提供了加密/解密协议。通过串口通信电视与电源通信,指令由电源发出,通过解析电源的信息进行对应的工作。在发送信息和接受信息部分,是放在电视开机后的主线程中,实时监控电源状态。
串口通信流程&注意事项
-
创建线程在线程内进行串口之间的收发
pthread_create(&t0, NULL, print_a, NULL)
遇到的问题:在一开始创建线程的时候,调用了
pthread_join()
函数,主线程需要等子线程完成操作后才会执行。由于子线程是电视开机后一直在执行,随时会接收数据发送数据,因此导致主线程没办法执行,电视死机没办法正常操作。pthread_join(t0, &result) //参数 :t0 : 线程标识符,即线程ID,标识唯一线程。 // result: 用户定义的指针,用来存储被等待线程的返回值。
-
打开串口
//函数原型:int open(constchar*pathname,intflags,mode_tmode);返回值:成功则返回文件描述符,否则返回-1 open(Dev,O_RDWR | O_NOCTTY | O_NONBLOCK)
对于open函数来说,第三个参数仅当创建新文件时(即 使用了
O_CREAT
时)才使用,用于指定文件的访问权限位。pathname 是待打开/创建文件的POSIX
路径名(如/home/user/a.cpp)
;flags 用于指定文件的打开/创建模式。 -
设置波特率
-
设置校验位
-
读写加异常判断以及加锁
因为pthread
并非Linux
系统的默认库,而是POSIX
线程库。在Linux
中将其作为一个库来使用,因此加上 -lpthread
(或-pthread
)以显式链接该库。函数在执行错误时的错误信息将作为返回值返回,并不修改系统全局变量errno
,当然也无法使用perror()
打印错误信息。
2. 关于解决死机问题、分析coredump
文件
-
在死机的时候,
coredump
打开的情况下,U盘会有一个coredump
文件生成。
把coredump
文件和umf.gdb
文件放在一起。路径:RDA512C_Release_0228\aps\application\s2tek\formal
在此路径下执行:mipsel-s2-linux-uclibc-gdb umf.gdb XXXXX
(coredump
文件名)
就可以执行(gdb
)bt
等命令查看死机的具体位置。
问题现象:当菜单语言为泰语时,设置休眠时间后会导致死机
通过分析
coredump
文件,发现问题出在一个字符串转换的地方(之前修改把5 time修改为time.),在转换字符串时只申请了10个字节空间,经过百度查资料得知泰语一个字符占3个字节,因此此处造成了越界。
3. 电视通过电源供电,显示电源剩余电量
-
菜单部分
先用PS做好各个电量的电池图标,根据由电压转化后的电量值刷新控件,显示对应的图标。
该接口放在
OnTimerUpdate
函数中定时刷新,因为要实时监测电源的电量。 -
代码部分
首先通过获取的电压值,根据文档说明范围转化成电量值(10%、20%.....)后刷新显示到画面上。因为之前没有加异常处理,导致数值异常时界面上的数据会乱码。