久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 5486|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

KT0810SG調(diào)試過程與程序(FM收音芯片)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:71922 發(fā)表于 2015-1-10 20:08 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
全志A20的板子,記錄下來,方便自己隨時(shí)復(fù)習(xí)。
自己按照以前熟悉的驅(qū)動(dòng)框架模型來寫,發(fā)現(xiàn)測(cè)試時(shí) echo 1 > /dev/KT0810SG  write()函數(shù)會(huì)一直被調(diào)用。

測(cè)試I2C讀寫時(shí),發(fā)現(xiàn)讀芯片ID的數(shù)值總是 0xFF 經(jīng)過一上午的折騰,確定是硬件問題,I2C的上拉電壓只有2.5V,改成3V 成功讀取ID值 == 0xb002

下午的時(shí)候,在執(zhí)行初始化 KT0810SG 函數(shù)時(shí),需要檢測(cè)晶振和System PLL 時(shí)鐘是否準(zhǔn)備好,但總是發(fā)現(xiàn) System PLL 未準(zhǔn)備好,

原因?yàn)?A20的提供的 32.768KHz 時(shí)鐘沒有送到 KT0810SG 芯片里,是硬件焊接錯(cuò)誤。 已經(jīng)解決。

現(xiàn)在 遇到的問題是,芯片沒有聲音輸出。
昨晚回去之后懷疑I2C寫寄存器 的函數(shù)有問題,沒有把數(shù)據(jù)寫進(jìn)去。

早上調(diào)試了很多次,發(fā)現(xiàn)有一個(gè)奇怪的現(xiàn)象,那就是寫的時(shí)候 高低字節(jié)不需要調(diào)換,但是讀取出來的時(shí)候需要調(diào)換高低字節(jié)。
中午,設(shè)置了8980頻率,居然有聲音了。后來發(fā)現(xiàn)有些頻率是沒有聲音的,所以最好多試試幾個(gè)頻率。
下午遇到的問題是,收到的臺(tái)數(shù)很少且噪音很大。懷疑是硬件問題,因?yàn)槟壳爸皇钦{(diào)試芯片,所以芯片都是直接在板子上飛線到芯片上的。干擾性會(huì)很大。

總結(jié):
        完全可以按照自己以前的驅(qū)動(dòng)框架寫fops方式:
        一開始 write()會(huì)一直被調(diào)用可能是因?yàn)?echo命令會(huì)判斷 如果寫入失敗就會(huì)一直寫,而write()剛好被我寫成返回0。
        1、先拿到原廠代碼,最好要原廠提供完整的 demo 源碼工程。
        2、大概了解原廠代碼,分析 demo 源碼的執(zhí)行流程以及各個(gè)函數(shù)的功能
        3、修改sys_config.fex 啟用使能I2C通道2
        4、編寫I2C驅(qū)動(dòng)框架,修改 原廠代碼的I2C讀寫函數(shù)(移植過程)
        5、在驅(qū)動(dòng)中根據(jù) demo 源碼分析出來的流程去調(diào)用原廠代碼 若不知道流程,最好詢問原廠。
        6、預(yù)留接口給上層。如設(shè)置收音頻率等 可以用 echo 123 >設(shè)備節(jié)點(diǎn)文件 來調(diào)試
       
        問題點(diǎn):
                1、KT0810SG 是采用I2C協(xié)議通訊,在測(cè)試I2C讀寫時(shí),發(fā)現(xiàn)讀取芯片ID總是0xFF,讀取其他的寄存器的值也不對(duì)。
                        硬件問題:I2C的SDA、SCL線電壓只有2.5V,需要提升至3V,MCU的高電平在3.3V左右
                       
                2、KT0810SG 在 KT_FMInit(void) 初始化總是失敗。
                        uchar KT_FMInit(void)                                        //0->Fail 1->Success
                        {
                                ...
                                ...
                                for (i=0;i<INIT_FAIL_TH;i++)
                                {
                                        Delay_ms(500);
                                        regx=KT_Bus_Read(0x12);                                               
                                        if ((regx&0x8800)!=0x8800)                // 查看芯片手冊(cè),這里檢測(cè)的是晶振和System PLL 是否準(zhǔn)備好
                                                continue;
                                        break;
                                }
                                if (i==INIT_FAIL_TH)
                                        return(0);
                                ...
                                ...
                        }
                        KT0810SG 的時(shí)鐘是由 A20 提供,32.768KHz 。
                        硬件問題:時(shí)鐘線接錯(cuò)位置,導(dǎo)致 A20 提供的時(shí)鐘沒法送到 KT0810SG 。
                        檢測(cè)方法:將時(shí)鐘線斷開,示波器去測(cè) A20 引腳看是否有頻率,現(xiàn)場(chǎng)是有的,但是接回芯片頻率就不正常。
                       
                3、根據(jù)原廠流程以及寫好驅(qū)動(dòng),但是沒有雜音輸出。
                        可能的原因:I2C 寫寄存器不成功,導(dǎo)致初始化失敗。 還有一個(gè)可能性就是 設(shè)置的頻率剛剛好沒聲音輸出,可以多設(shè)置幾個(gè)頻率試試。
                       
        注意點(diǎn):
                在修改原廠驅(qū)動(dòng)的I2C讀寫函數(shù)時(shí),因?yàn)?KT0810SG芯片的寄存器是16位 ,I2C讀函數(shù)讀出的數(shù)據(jù)是兩個(gè)字節(jié)
                需要將兩個(gè)字節(jié)的數(shù)據(jù)調(diào)換即高字節(jié)和低字節(jié)互換 0x1234 => 0x3412 而I2C寫函數(shù)則不需要交換
       
修改增加裸板程序的I2C 讀寫函數(shù):(奇怪的是讀的時(shí)候高八位和低八位互換才能用。而寫的時(shí)候則不需要調(diào)換)
        static unsigned char i2c_write_reg(unsigned char reg,unsigned short data)                // 可用
        {
                unsigned char buf[3] = {0};
                unsigned short a, b;
                printk("%s - %s  reg = 0x%X  data = 0x%X o(∩_∩)o~~!\n", FMMSG, __func__, reg, data);
                /*
                // 高低八位互換
                buf[0]        = reg;
                buf[1] = ((data << 8) & 0xFF00) >> 8;        // 因?yàn)閐ata是兩個(gè)字節(jié) 而buf[] 是一個(gè)字節(jié)
                buf[2] = (data >> 8) & 0x00FF;
                */
                // 高低不換  這里不能對(duì)換 否則無法收到臺(tái)。
                buf[0]        = reg;                                                        // 寄存器
                buf[2] = ((data << 8) & 0xFF00) >> 8;
                buf[1] = (data >> 8) & 0x00FF;
                printk("+++write true buf[0]=0x%X buf[1]=0x%X buf[2]=0x%X\n", buf[0], buf[1], buf[2]);

                i2c_master_send(FM_dev->FM_client, buf, sizeof(buf));
               
                return 0;
        }       
        static unsigned int i2c_read_reg(int reg)                        // 可用
        {
                int ret;
                unsigned short  data = 0, a = 0, b = 0;

                struct i2c_msg msgs[] = {
                        {
                                .addr        = FM_dev->FM_client->addr,
                                .flags        = 0,
                                .len        = 1,                                                        // 1個(gè)字節(jié)
                                .buf        = &reg,                                                        // 寄存器
                        },
                        {
                                .addr        = FM_dev->FM_client->addr,
                                .flags         = I2C_M_RD,
                                .len        = 2,                                                        // 兩個(gè)字節(jié)的空間
                                .buf        = &data,                                                // 用于存放讀取出來的數(shù)據(jù)
                        }
                };
               
                ret = i2c_transfer(FM_dev->FM_client->adapter, msgs, 2);
                if(ret < 0)
                        printk("read error~~~~~~~~~~~~~~~");
                else
                {
                        printk("read ok reg = 0x%x data = 0x%x!!!\n", reg,  data);
                        a = (data << 8) & 0xFF00;                                        // 有些奇怪,需要互換高低八位
                        b = (data >> 8) & 0x00FF;
                        data = a | b;  
                        printk("read ok reg = 0x%x data = %d | %d = 0x%x!!!\n", reg,  a, b , data);
                }
                return data;
        }
       
       
       
意外收獲:
        FM5807驅(qū)動(dòng)流程分析(這種方式并沒有實(shí)現(xiàn)fops結(jié)構(gòu)體來給上層提供接口,有些地方不是很明白,也算是一種有趣的實(shí)現(xiàn)方式):
        原來的驅(qū)動(dòng)寫的有些亂,我簡(jiǎn)化了一些。
       
        #define FM_CHRDEV_NAME "KT0810SG"
        module_init(FM_init);                        // 定義驅(qū)動(dòng)入口方式
        module_exit(FM_exit);                        // 定義驅(qū)動(dòng)出口方式
       
        //函數(shù)入口
        static int __init FM_init(void)
        {
                // 讀取配置文件
                // 保存從配置文件里面選擇的I2C通道
                // 設(shè)置輸出32.768KHz時(shí)鐘
                twi_id = 2;                                // 會(huì)用在FM_detect()
                // 加載I2C驅(qū)動(dòng)
                i2c_add_driver(&FM_drv);
                return 0;
        }
        // 全局變量
        static __u32 twi_id = 0;
       
        // 定義FM I2C驅(qū)動(dòng)結(jié)構(gòu)體
        struct i2c_driver FM_drv = {
                .class = I2C_CLASS_HWMON,                // 不能忽略,表示去哪些適配器上找設(shè)備
                .detect =         FM_detect,                // 該函數(shù)確定能否找到address_list里的設(shè)備
                .probe =        FM_probe,                // 如果匹配就調(diào)用probe
                .remove =        __devexit_p(FM_remove),
                .driver = {
                        .name = "FM_drivce",
                        .owner = THIS_MODULE,
                },
                .id_table = FM_id,                        //支持的設(shè)備列表
                .address_list = normal_i2c,                // I2C設(shè)備地址列表
        };
        // FM芯片的設(shè)備地址
        static const unsigned short normal_i2c[2] = {0x37, I2C_CLIENT_END};
       
        // 實(shí)現(xiàn)FM_detect()
        static int FM_detect(struct i2c_client *client, struct i2c_board_info *info)
        {
                struct i2c_adapter *adapter = client->adapter;
                int ret = 0, i =0;
               
                printk("%s - %s Test o(∩_∩)o~~~ !\n", FMMSG, __func__);
                // 檢查功能
                if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                        return -ENODEV;               
                       
                if(twi_id != adapter->nr)        // 匹配FM芯片所在的I2C通道 匹配成功才能回調(diào)Proc()
                        return -ENODEV;
                else
                        strlcpy(info->type, FM_CHRDEV_NAME, I2C_NAME_SIZE);// 若匹配則拷貝名字 成功匹配會(huì)調(diào)用probe()函數(shù)
                        return 0;
        }       
       
        // 實(shí)現(xiàn)FM_probe()
        int FM_probe(struct i2c_client *client, const struct i2c_device_id *device_id)
        {
                int ret;

                if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
                {
                        printk("%s_check_functionality_failed!\n", __func__);
                        return -ENODEV;
                }
                       
                FM_dev = kmalloc(sizeof(struct FM_device*), GFP_KERNEL);
                if( NULL == FM_dev)
                {
                        printk("%s - %s no memory for kmalloc!\n", FMMSG, __func__);
                        return -ENOMEM;
                }
               
                // 獲取Client 保存到全局變量中
                FM_dev->FM_client = client;
                FM_dev->FM_client->driver = &FM_drv;
               
                class_register(&FM_attrs_class);      // 重點(diǎn),這里會(huì)注冊(cè)在/sys/class/設(shè)備名 具體看 FM_attrs_class
                       
                INIT_DELAYED_WORK(&open_pa, open_pa_func);        // 這里是工作隊(duì)列 初始化
                __cancel_delayed_work(&open_pa.work);                // 取消調(diào)度
                schedule_delayed_work(&open_pa.work, 100);        // 100ms后調(diào)用 open_pa_func()

                return 0;
        }
       
        struct delayed_work open_pa;
        // 定義
        struct FM_device{
                struct i2c_client *FM_client;
                struct device *FM_device;
                struct class *FM_class;
        };

        struct FM_device *FM_dev;

        // echo 10 >/sys/class/kt0810sg/cmd                命令會(huì)將參數(shù)傳遞到  FM_cmd_store()
        // 這里不太明白 FM_status_sho()、FM_cmd_show() 作用
        static struct class_attribute        FM_attrs[] = {
                __ATTR(status, 0777, FM_status_show, FM_status_store),           // 構(gòu)成 /sys/class/kt0810sg/status 節(jié)點(diǎn)
                __ATTR(cmd, 0777, FM_cmd_show, FM_cmd_store),                // 構(gòu)成        /sys/class/kt0810sg/cmd        節(jié)點(diǎn)
                __ATTR_NULL
        };
        static struct class FM_attrs_class = {
                .name = "kt0810sg",                                        // 構(gòu)成/sys/class/kt0810sg 目錄
                .class_attrs = FM_attrs,
        };
       
        // 工作隊(duì)列
        static void open_pa_func(struct work_struct *work)
        {
                printk("-------------------open pa \n");
                // 這里用于關(guān)pa  于框架沒有實(shí)際意義。
        }
       
        // 實(shí)現(xiàn)上面需的回調(diào)函數(shù) echo 10 >/sys/class/kt0810sg/cmd 會(huì)傳進(jìn)來
        static ssize_t  FM_cmd_store(struct device *dev, struct device_attribute *attr, const char *buf, ssize_t count)
        {
                unsigned long data = 0;
                int command = 0;
               
                printk("%s - %s Test o(∩_∩)o~~~ !\n", FMMSG, __func__);
                strict_strtoul(buf, 10,  &data);
                command = data;
                printk("%s_Revc = %d\n", __func__, command);
               
                // 在 0-15 之間則表示直接設(shè)定音量
                if(command >= FM_CMD_SETVOLUMEMIN && command <= FM_CMD_SETVOLUMEMAX )
                {
                        printk("Cmd is Set Volume %d \n", command);
                        KT_FMVolumeSet(command);
                        goto retu;
                }
               
                // 在 8700-10800 之間則表示直接設(shè)定頻率
                if(command >= FM_CMD_FREQMIN && command <= FM_CMD_FREQMAX )
                {
                        printk("Cmd is Set Freq %d \n", command);
                        KT_User_FMTune(command);
                        KT_FMUnMute();
                        goto retu;
                }

                // 剩余的表示各種命令
                switch(command)
                {
                        case FM_CMD_AUTOSCAN:                                                                // 自動(dòng)搜臺(tái)
                                printk("cmd = FM_CMD_AUTOSCAN ---> OK\n");
                                ScanFM();
                                break;
                        case FM_CMD_SELECTUP:                                                                // 上一個(gè)臺(tái)
                                printk("cmd = FM_CMD_SELECTUP ---> OK\n");
                                break;
                        case FM_CMD_SELECTDOWN:                                                                // 下一個(gè)臺(tái)
                                printk("cmd = FM_CMD_SELECTDOWN ---> OK\n");
                                break;
                }
        retu:
                return count; // 返回值必須指定成功接收了多少個(gè)字節(jié),若返回0 那么此函數(shù)會(huì)一直被回調(diào)
        }

        static ssize_t FM_cmd_show(struct device *dev, struct device_attribute *attr, char *buf)
        {
                printk("%s - %s Test o(∩_∩)o~~~ !\n", FMMSG, __func__);
                return 1;
        }

        static ssize_t FM_status_store(struct device *dev, struct device_attribute *attr, const char *buf, ssize_t count)
        {
                printk("%s - %s Test o(∩_∩)o~~~ !\n", FMMSG, __func__);
                return count;
        }

        static ssize_t FM_status_show(struct device *dev, struct device_attribute *attr, char *buf)
        {
                printk("%s - %s Test o(∩_∩)o~~~ !\n", FMMSG, __func__);
                return 1;
        }
        // 實(shí)現(xiàn) 退出函數(shù)
        int FM_remove(struct i2c_client *client)
        {
                printk("%s - %s Test o(∩_∩)o~~~ !\n", FMMSG, __func__);
                return 0;
        }
        // 這很關(guān)鍵,是否能重復(fù)加載 該ko文件取決于它。
        static void __exit FM_exit(void)
        {
                printk("%s - %s Test11 o(∩_∩)o~~~ !\n", FMMSG, __func__);

                __cancel_delayed_work(&open_pa.work);
                class_unregister(&FM_attrs_class);// 必須卸載  會(huì)刪除 /sys/class/kt0810sg 目錄 不然下次加載就無法創(chuàng)建
                i2c_del_driver(&FM_drv);                // 刪除I2C驅(qū)動(dòng)
                kfree(FM_dev);                                // 釋放。                               
        }
               




分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 四虎永久免费黄色影片 | 91伊人| 99久久婷婷国产亚洲终合精品 | 欧美在线一区二区视频 | 亚洲精品乱 | 一区二区三区四区毛片 | 韩国av一区二区 | 超碰在线亚洲 | 成人久久18免费网站麻豆 | 欧美激情久久久 | 国产在线拍偷自揄拍视频 | 国产精品久久99 | 日本一区二区不卡 | 国产成人啪免费观看软件 | www日本高清视频 | 一级免费视频 | 男人的天堂久久 | 亚洲一区二区日韩 | 欧美一级一 | www国产成人免费观看视频,深夜成人网 | 中文字幕免费 | 国产精品久久久久久久久动漫 | 亚洲黄色成人网 | 成人黄色电影在线播放 | 成年人的视频免费观看 | 国产精品一区久久久 | 狠狠亚洲 | 99精品国自产在线 | 四虎影院欧美 | 亚洲国产精品99久久久久久久久 | 男女视频在线观看免费 | 免费av播放 | 亚洲日本欧美日韩高观看 | 国产精品久久久久久久午夜片 | 伊人久久在线 | 一区二区三区欧美在线观看 | 久久99精品久久久久久国产越南 | 成人网av | 精品国产乱码久久久久久丨区2区 | 欧美精品一区二区三区在线播放 | 国产视频久久 |