Pages:  1 2 CDMA – Page 2 – lytsing's Blog

Category: CDMA

  • 使用Ruby在windows下通过串口自动测试AT

    原文http://deli.xmu.me/?p=7,放在Wayhome mm给我提供的空间,现在已经无法访问。

    手机芯片厂家常常会更新模组软件版本,在发给客户之前,一般先对AT进行一次测试。手机设计公司收到新的软件版本,烧入Flash, 也要对模组的AT进行一次测试,验证是否正常,比较与上个版本是否有差别。手工一个个验证,乏味,容易对工作失去兴趣。

    从http://rubyinstaller.rubyforge.org 可以下载 Ruby One-Click,现在最新版本安装软件为ruby186-27_rc2.exe。安装过程中,把RubyGems也勾上。在windows上用ruby访问串口,可以用win32ole模块,也可以用ruby-serialport,后者是跨平台的。安装ruby-serialport前要做一些准备工作,确认有装Microsoft’s Visual C++ 或Borland’s C++编译器,这里以VC6.0为例。
    (1)设置环境变量:

    PATH=C:\Program Files\Microsoft Visual Studio\VC98\Bin
    INCLUDE=C:\Program Files\Microsoft Visual Studio\VC98\Include
    LIB=C:\Program Files\Microsoft Visual Studio\VC98\Lib

    (2)安装ruby-serialport:
    开始 -> 程序 -> Ruby-186-27 -> RubyGems -> RubyGems Package Manager,输入:
    gem install ruby-serialport
    如果提示找不到mspdb60.dll, 请到http://www.dll-files.com 找,把它放到
    C:\Program Files\Microsoft Visual Studio\VC98\Bin
    接下来,就可以写一个测试脚本了。

    # filename: test.rb
    require 'rubygems'
    require 'serialport'
    
    # 0 is mapped to "COM1" on Windows, and 5 is COM6, 115200 is baud rate
    sp = SerialPort.new(5, 115200)
    sp.write "AT\r\n"
    sleep(0.2)
    puts sp.read   # hopefully "OK" ;-) 

    注意:如果没有sleep,可能会收不到”OK”响应。

    如果你弄了老半天,ruby-serialport也没装得上,那就用用win32ole,美其名曰:Windows Automation。win32ole是Masaki Suketa编写的Ruby扩展,是标准ruby发行版本的一部分。
    现在写一个mscomm.rb文件

    #! /usr/bin/env ruby
    #
    # filename: mscomm.rb
    # @date 2009.5.16
    #
    
    require 'win32ole'
    
    class MSCOMM
      def initialize(port)
        @serial = WIN32OLE.new("MSCOMMLib.MSComm")
    
        @serial.CommPort = port
        @serial.Settings = "115200,N,8,1"
        @serial.InputLen = 0
        @serial.PortOpen = true
      end
      def write(str)
        @serial.Output = str
      end
      def read
        str = @serial.Input
        str
      end
      def close
        @serial.PortOpen = false
      end
      def serial
        @serial
      end
    end

    再写一个简单的测试用例test_comm.rb:

    #! /usr/bin/env ruby
    #
    # filename: test_comm.rb
    # @date 2009.5.16
    #
    
    require 'mscomm.rb
    
    comm  = MSCOMM.new(6)
    comm.write("AT\r\n")
    sleep(0.2)
    puts comm.read
    comm.close

    和上面的test.rb用法一样,没什么神秘感,是不是很简单呢 🙂

    再加个查询信号的AT,在test_comm.rb倒数第二行加上
    comm.write(“AT+CSQ?\r\n”)
    sleep(0.2)
    puts comm.read
    要是很多很多…想想,还是写个函数吧.

    def exec_cmd(comm, cmd)
      comm.write("#{cmd}\r\n")
      sleep(0.2)
    
      begin
        result = comm.read
        result = "Command not support\n" if result.include?("ERROR\n")
      rescue
        result = "Writing serial port error\n"
      end
    
      puts result
    end

    于是,test_comm.rb中间部分的可以可以写成:
    exec_cmd(comm, “AT”)
    exec_cmd(comm, “AT+CSQ?”)

    可是每添加一个AT… 还是得写一串的exec_cmd(comm… 。Dave Thomas大师说过:”Don’t Repeat Yourself!” ,这是ruby的设计理念。假设所有的AT都放在另外一个文件呢?我们也可以一个个读取出来。不过现在考虑的是暂时放在同个文件,那就定义一个字符串数组:
    atcmd = {
    ‘AT’,
    ‘AT+CSQ?’,
    ‘AT+CREG?’
    }
    也可以这样写:
    Atcmd = %w{
    AT
    AT+CSQ?
    AT+CREG?
    }

    第二种对于添加AT比较方便,但缺点是AT不能出现空格。那么test_comm.rb 现在又可以写为:
    atcmd.each {|at| exec_cmd(comm, at) }

    可是,有的AT需要花一些时间才有响应。之前都默认是0.2秒,好吧,重新定义:
    def exec_cmd(comm, cmd, timeout = 0.2)

    sleep(timeout)

    end

    于是,就可以这样使用
    exec_cmd(comm, “AT+CDV=10000”, 3)
    exec_cmd(comm, “AT+CLCC?”)
    exec_cmd(comm, “AT+CHV”)
    exec_cmd(comm, “AT+CPOF”, 2)
    exec_cmd(comm, “AT+CPON”, 5)

    像这样的一组AT,具有依赖顺序而每个AT的响应时间又不一样,我们只能根据不同的情况对AT做分类,写不同的测试脚本。

    再回头看看 test_commm.rb,如果我们想把输入与输出都放在同一个Excel表格,那该如何写呢?原理一样,依旧用win32ole:

    #! /usr/bin/env ruby
    #
    # filename: excel.rb
    # @date 2009.5.16
    #
    
    require 'win32ole'
    
    class Excel
      def initialize(filename = nil)
    @excel = WIN32OLE.new("excel.Application") # create Excel object
    
        @excel.Visible = TRUE
        if (filename == nil)
          @workbook = @excel.Workbooks.Add() # create new file
        else
          @workbook = @excel.Workbooks.Open(filename) # open exist file
        end
      end
    
      def setvalue(pos, data)
        @excel.Range(pos).Value = data
      end
      def save
        @excel.Save()
      end
      def close
        @excel.Quit()
      end
      def excelobj
        @excel
      end
    
    end # end of class

    我们重新写个test_cdma_at.rb

    #! /usr/bin/env ruby
    #
    # test_cdma_at.rb
    # @date 2009.5.16
    #
    
    require 'mscomm.rb'
    require 'excel.rb'
    
    def exec_cmd(comm, cmd)
      comm.write("#{cmd}\r\n")
      sleep(0.2)
    
      begin
        result = comm.read
        result = "Command not support\n" if result.include?("ERROR\n")
      rescue
        result = "Writing serial port error\n"
      end
    
      return result
    end
    
    atcmd = %w{
    AT
    AT+CPIN?
    AT+CPINC?
    AT+CSQ?
    AT+CREG=2
    AT+CREG?
    AT+VMCC?;+VMNC?
    AT+CPBS=?
    AT+CPBS?
    AT+CPBS="ME"
    AT+CPBS?
    AT+CPBW=3,13544049382,"violet",0
    AT+CPBR=3
    AT+CMGS=13544049382,"Hello!"
    AT+CMGW=,13544049382,"Hi!"
    }
    
    comm  = MSCOMM.new(6)
    excel = Excel.new("d:\\Book1.xls")
    
    i = 1
    
    atcmd.each {|at|
      excel.setvalue("a#{i}", at)
      excel.excelobj.Range("b#{i}").Value = exec_cmd(comm, at)
      i += 1
    }
    
    comm.close
    excel.save
    excel.close

    代码不难理解,就是把AT输入放在Excel表格某行的A列,响应写入某行的B列,仅如此而已。

    脚本测试也不是万能的,AT本身具有随意性,写脚本意在减轻编码工作量,避免重复机械的劳动。

  • 用gsmmux 测试via cdma多路复用功能

    现在杭州的威盛已实现了GSM协议07.10 multiplexer。
    gsmmux 可以在 developer.berlios.de/projects/gsmmux/ 上获得.按照说明安装就可以了。
    代码默认用的是AT+CMUX开启功能,而CMUX在CDMA另有别用,所以他们就用VMUX来替代,在代码里,把CMUX改为VMUX,重新编译。

    运行 mux,得到两个虚拟逻辑串口 /dev/mux0 /dev/mux1,
    开一个终端1 cat /dev/mux0 观察数据
    再开一个终端2输入 echo -e “AT\r\n” > /dev/mux0
    终端1有 “OK” 响应就行了。
    同样的方法测试 mux1.

    核心代码在 gsm0710.c

    简单说明一下。
    1190 行的 main函数,读取命令行参数,
    1272 行 daemonize(_debug) 设置为unix下经典后台程序,下来是设置信号中断机制。

    openDevicesAndMuxMode 函数 打开modem 初始化,发 “AT+VMUX=0” 进入mux模式。

    MUX启动过程
    主机发: AT+VMUX=0
    模块回复:OK /*进入MUX模式*/
    主机发: F9033F011CF9 /*建立DLC0*/
    模块回复:F9037301D7F9
    主机发: F9073F01DEF9 /*建立DLC1*/
    模块回复:F907730115F9
    F901EF09E305070D9AF9 /*DLC1 MSC 命令*/
    主机发: F90B3F0159F9 /*建立DLC2*/
    模块回复:F90B730192F9
    F901EF09E3050B0D9AF9 /*DLC2 MSC 命令*/

    /*…开始进行MUX协议的数据传输…*/

    在 1106行的openDevicesAndMuxMode,
    三次(一般是三次,可以设置)打开 /dev/ptmx ,得到三对主从终端,通过符号链接,创建两个虚拟串口

    /dev/mux0 /dev/mux1 这时就可以像正常访问串口一样访问它们,一般 /dev/mux0
    用来专门发AT, /dev/mux1 用来发送数据业务,比如pppd 拨号上网。

    数据流大概是这样的
    /dev/ttyS0 <---> | /dev/pmux <----> /dev/mux0 | <----> at command
    /dev/ttyS0 <---> | /dev/pmux <----> /dev/mux1 | <----> cdma pppd

    下面
    1162行 – 1166行
    for (i = 1; i <= numOfPorts; i++)
    {
    sleep(1);
    write_frame(i, NULL, 0, SABM | PF);
    syslog(LOG_INFO, "Connecting %s to virtual channel %d on %s\n", ptsname(mux_fd[i-1]), i, serportdev);
    }

    write_frame(i, NULL, 0, SABM | PF); 实现的就是发
    F9033F011CF9 /*建立DLC0*/
    F9073F01DEF9 /*建立DLC1*/
    F90B3F0159F9 /*建立DLC2*/

    然后进入 while循环,用selelct 实现 i/o 多路复用,
    (1)检查物理串口,如果有可读的数据,读取放到buffer,然后解析数据帧,并发送到虚拟逻辑串口
    (2)检查虚拟逻辑串口,如果有可读的数据,构造数据帧,写入物理串口。

    就这样,基本原理是数据分组打包与解包。

  • 在MTK平台上部署CDMA

    原文在 2009/10/14 发表于broncho论坛,转载请写出处,谢谢!

    在MTK平台上部署CDMA

    [注] 我们用的MTK版本是6225 ,CDMA 模组是威盛via的。此项目没有量产,代码放在那也是加密冷藏没有价值,写思路出来与大家讨论关于程序设计。本人不是MTK专家,有分析不对之处,请指出,谢谢。

    [硬件篇]
    加一个cdma模组,具体操作本人不知道,就不讲述了。

    [软件篇]
    让我们来分析一下。既然在MTK平台,上层应用最好不要改动,就只能在底层修改。在L4层我们发现,MTK提供了mmi接口,那么我们就有机会重写这些函数,这个就是最大前提。对于CDMA,要对AT做封装,适配于L4(适配器设计模式)。我们可以开一个task,专门处理CDMA AT,串口数据的读取。考虑到CDMA的AT比较弱,特别是三方通话,呼叫保持,对AT的处理,得用较多全局变量来保存上下文。

    代码目录:

    代码: 全选
    Vendor -|
    |- ATCMD -| at_cdma
    | at_gsm
    | at_l4a
    | at_thread

    At_thread 主要是处理AT的线程.
    首先创建一个task.
    /* task */
    void AT_Cmd_main(task_entry_struct * task_entry_ptr); 一个while 循环,处理消息。

    At_gsm 是一个参考物,为了测试cdma方便,我们自己实现MTK的AT封装。At_cdma 是cdma编解码目录。可以说,at_cdma继承于at_gsm,两者有统一的接口(主要是gsm标准,cdma适应于gsm),所以很多的工作,在at_cdma做了很大的调动。如果实现和gsm不一样,我们就要rewrite接口实现,基于函数替换。

    上面已有提到过,我们要重新实现mtk提供的l4a层,那么at_l4a 就是干这样的活了。为了不直接在mtk l4a的函数里修改,我们采用include 的方式。所有的处理函数放在AT_L4c_Funs_V07B.c
    写我们自己的 l4a_callback_ex.c ,在ps\l4a\src\l4a_callback.c 的末尾 #include “l4a_callback_ex.c” ,然后在 l4a_main.c的 l4a_recvmsg(ilm_struct *ilm_ptr)里加以区别:如果 l4c_current_mod_id == l4c_atcmd_mod_id,就调用我们实现的接口函数处理cdma,否则,就调用mtk提供的处理gsm(不变)。

    串口通讯的处理
    利用w32_uart.c 里提供的一些函数,打开串口,设置波特率,数据控制流等参数。
    对数据的发送,接收,加入休眠模式,以免功耗过大。

    AT的处理
    在MTK上对CDMA AT的处理,则采用赤裸裸的封装,源于有开发过Firebird BBS的经历,我更喜欢这种最原始的美,不要和我谈什么软件架构,设计模式的,考虑越多,项目越难以进展,领导只关心是否能跑起来,不然项目取消,大家散伙吧。说远了。AT具有随意性,我们假设发出请求的AT,就有相应的响应,设一个定时器,来处理超时问题。如果超时了,我们再发AT,如果有OK回应,就说明模组还能正常工作,那么这个AT的请求就是失败的。对自动上报的消息的处理,在获取AT响应时,我们判断是自动上报的消息,则当场处理,继续读下一行数据,直到有期望的响应值。如果请求发出一个AT, 既有自动上报消息,又有自身的请求响应,该如何处理呢?举个例子,查询网络注册情况,发出AT+CREG?后,
    +CREG:1,0×3614,0×2

    +CREG:1,0×3614,0×2

    +CREG: 2,1,0x00C3,0x00AD

    我们发现,请求的响应有4个参数,而自动上报的有三个参数。再举个例子:
    开机时,模块会自动开启一个15min的计时器,如果15分钟内没有收到AT+LCT指令,模块就自动关机,如果收到了该指令,15min的计时器又重新开始计时。所以我们看到+LCT主动上报时,就发出AT+LCT。问题来了,我们打电话时,查询CLCC,那么问题出现如下:
    AT+CLCC?
    +LCT
    AT+LCT
    +CLCC:1,0,1
    ok
    ok

    两个响应消息交错在一起,就难以分析了,我们就得保证消息串化。
    等等。AT响应的处理,大部分工作是解析字符串,取出你想得到的数据,填充结构体。总之,在做AT处理时,要反复测试,考虑很多的异常,这便是软件设计的难点之一。

    AT层上GSM 与CDMA的差异
    就列举几个比较重要的差别,很多小细节还是非常多的,请参阅厂家的AT SPEC。
    (1)AT测试命令,比如信号查询 GSM是AT+CSQ;CDMA则是 AT+CSQ? 很多种情况如此,CDMA在后面要加上一个“?”号。
    (2)AT响应字符串
    比如, GSM:
    +CCLK: “04/01/19,15:38:32″
    +CCLK:后面有空格
    CDMA:
    +CCLK:2008/8/6,13:57:5,3
    +CCLK: 后面没有空格
    响应字符串解析函数必须能处理空格问题。可以写一个类似于sscanf的函数,专用解析AT响应字符串。
    (3) CDMA中”UIM”的字符串,这个主要出现在电话本与短信。MTK上是”SIM” 对应 0, “ME”对应1;CDMA把”UIM”对应为0即可。此外,短信与电话本,gsm从1开始读取,cdma则从0开始读。
    (4) TE特征字符串设定
    GSM: AT+CSCS
    CDMA没有这方面的设定,默认为UNICODE,在电话本处理中,AT请求和响应要特别指出.
    (5) 短信的PDU编解码完全不同.请参考 GSM0707与3gpp2 的C.S0015-B
    (6) 电话状态查询CLCC,CDMA提供的功能很弱,不管有没有来电,拨出电话,查询CLCC总有一个+CLCC:响应,GSM则不然。CDMA处于三方通话,呼叫保持,呼叫等待时,查询CLCC也是只有一路+CLCC:响应,这样,我们得自己构建适应于GSM的CLCC,维护,更新CLCC列表。

    开关机流程
    (1)开机
    +VPUP
    模块上报开机启动标志,也可根据该命令判断模块重启,收到该命令后进行初始化设置. (模组有问题时,这个上报是不可靠的.)
    +MSStatus:0
    模块上报协议栈已打开。
    +VROM:1
    模块上报漫游状态(1 为非漫游状态)。 这个上报是不可靠的,就是说,并不是每次开机都会有这个上报。不过,如果出现这个,我们认为可以显示中国电信了,其他手机已开机就显示运营商,估计也是照这个来做。

    AT+ISF?
    查询模块初始化状态,2s~3s 执行一次
    +ISF:1
    模块初始化完成。

    接下来才可以对电话本,短信,通话记录初始化。

    由上,在+MSStatus:0与 +ISF:1之间,可以操作的行为有:
    (0)ATE0 软件已经默认关闭回显,该步骤可省。
    (1)注册网络与信号上报. AT+CREG=2 AT+ARSI=1
    (2)查询UIM是否插好?AT+CPIN?
    (3)查询信号 AT+CSQ?
    (4)查询国家码,网络码 AT+VMCC? AT+VMNC?
    (5)选择语音通道 AT+SPEAKER=0

    开机流程比较繁琐,要适应与MTK的启动流程,比如开启Pin码,手机密码,网络运营商的获取,信号强度上报等。一般情况下,模块加电后会自行开启协议栈。不插UIM卡启动,模块不会自行打开协议栈。需要输入PIN的情况下,只有在用户输入正确的PIN,模块才会开启协议栈。在以上任何情形下开机,模块都会上报(+VPUP),客户可以根据此上报来判断正常使用中的模块是否重启。
    (2)关机:
    由于关机需要在网络上进行一些登记操作,因此正常的关机步骤是建议+CPOF关闭协议栈后延迟一段时间断电。
    (3)飞行模式:
    模块实现飞行模式可以直接使用+CPOF关闭协议栈,+CPON打开协议栈返回正常模式。
    查询当前是否在飞行模式:+VPON.

    细节:
    长短信的发送:
    Cdma不能无缝的发送长短信,一般要等到 +CDS:上报才能发出下一条,一般情况下,发出一条短信过4-5秒后就有+CDS上报,不过,如果发给自己的话,这个时间会更长,我们采用延时30秒。一般如果发多条短信,第二条一般会返回+CDS:2,2,66 ,就是说,发不成功。我们要把它保存起来,重发一次。

    结尾
    当然,要做到符合最新电信需求规范,就比较困难了。MTK本是在GSM协议上做起来的,GSM于CDMA本来就存在很大的差别。说不定过不久MTK会发布C+G的版本,啥东西人家都帮你弄好了,就等着只改界面换个图片铃声吧。