【近期项目总结】海思、RDA、python项目总结

海思项目

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);  

对于获取文件名,我用到readdirclosedir两个函数。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一样全频点搜台,NTSCAirCable有两个不同的频点表,通过频点表进行搜台。

  • 盲扫功能解决方法:考虑盲扫和手动搜台功能相似,因此修改了手动搜台的接口底层逻辑,从上层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. ATVChannel lock会出现画面异常无法遮黑&解锁画面遮黑大概率解除


  • 问题1现象:节目lock后画面停止,输密密码菜单弹出,没有声音

初步分析:首先画面是锁定成功的,没有遮黑问题可能出在freezeWin时流程走的不对导致静帧参数错误导致。

问题现象是从锁定节目后复现的,锁台后锁定画面该部分的代码是在hi_mw_logic_lockmanager.cpphi_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算法的文件名必须是带路径的,否则只能检测当前目录下的图片,并不能遍历到子文件夹中。

大概思路如下:

  1. 获取所有文件名(带路径),采用递归遍历获取所有带路径的文件名,将文件名和带路径的文件名存入字典中。开始考虑用列表存,但是在通过append函数循环添加时,参数是list时报错,所以就用的字典存放,后面查询也方便。

  2. 通过字符串切片用"."进行分割出后缀,即文件格式得到所有的图片

  3. 拿到所有图片并带路径,循环两两比较图片,调用比较函数

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 XXXXXcoredump文件名)

就可以执行(gdbbt等命令查看死机的具体位置。

问题现象:当菜单语言为泰语时,设置休眠时间后会导致死机


通过分析coredump文件,发现问题出在一个字符串转换的地方(之前修改把5 time修改为time.),在转换字符串时只申请了10个字节空间,经过百度查资料得知泰语一个字符占3个字节,因此此处造成了越界。

 

3. 电视通过电源供电,显示电源剩余电量


  • 菜单部分

    先用PS做好各个电量的电池图标,根据由电压转化后的电量值刷新控件,显示对应的图标。

    该接口放在OnTimerUpdate函数中定时刷新,因为要实时监测电源的电量。

  • 代码部分

    首先通过获取的电压值,根据文档说明范围转化成电量值(10%、20%.....)后刷新显示到画面上。因为之前没有加异常处理,导致数值异常时界面上的数据会乱码。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值