物联网-移远M26模块OpenCPU开发第1讲

心心恋恋的基于移远M26模块设计的用于开发OpenCPU功能的板子终于完成,废话少说,直接上图:

物联网-移远M26模块OpenCPU开发第1讲_第1张图片

对于板子外观这里不作讨论,毕竟不是我设计的,期望能用吧。

可能是由于设计者经验不足的原因吧。我刚拿到板子的时候一看,心想坏了。貌似串口没有进行电平匹配,找来原理图纸一看,还真的没有进行电平匹配。呵呵,这个时候我还能说啥,自己搭电平匹配电路吧。我又打开了M26 OpenCPU硬件参考手册,找到串口设计章节,如下图:

物联网-移远M26模块OpenCPU开发第1讲_第2张图片

手册下载地址参见:M26 OpenCPU硬件设计参考手册  。

找来了需要的元器件,手工焊接,有点丑陋,不喜勿喷哈。成品如下图:

物联网-移远M26模块OpenCPU开发第1讲_第3张图片

由于使用的是笔记本电脑开发,所以找来一根以前工程使用剩下来的USB转TTL线,与自制电平匹配电路板连接起来,如下图:

物联网-移远M26模块OpenCPU开发第1讲_第4张图片

接下来我就将M26电路板上的串口引脚引出来连接至自制电平匹配电路板上,效果如下:

物联网-移远M26模块OpenCPU开发第1讲_第5张图片

好了,到此OpenCPU开发所需的硬件已经准备好了。我们就通电连上电脑试一下效果吧。

呵呵,果然,不可能那么顺利的。

连上后打开串口工具,给其发送AT指令,串口没有返回OK,而是直接返回AT,而且串口还会时不时的返回00。不过至少可以证明M26模块没有问题,我想着可能是供应商知道我们是要使用OpenCPU功能,直接烧录了测试代码吧。毕竟一上电板子上的指示灯不停的闪烁、蜂鸣器不停的响。那行,我就直接下载一次M26内核试一下。

打开QFlash 4.8烧录软件,选择好串口号和烧录文件,如下:

物联网-移远M26模块OpenCPU开发第1讲_第6张图片

操作方法参见qflash用户指导手册:Quectel_QFlash_OpenCPU_User_Guide_V1.0

工具下载地址:qFlash v4.8

点击Start按钮后,重新给电路板上电,看到下面的进度条在前进,很是激动,心想就要成功了。

呵呵,没错,又出问题了,此时一万只草泥马从心中经过。错误如下图:

物联网-移远M26模块OpenCPU开发第1讲_第7张图片

通过查看qflash用户指导手册中找到引起这个错误的原因,原来是下载时电压不稳定或者是下载线缆连接不稳定,如下图:

物联网-移远M26模块OpenCPU开发第1讲_第8张图片

好了,知道问题了就来解决问题吧。下载线缆连接肯定不会出错,毕竟串口调试助手一直收发都没有出问题,那就只能是电路板供电问题了,继续查看原理图。看到供电电路貌似也没有问题,这时我又犯愁了,到底怎么回事呢?这时,一个地方引起了我的注意,那就是电路板上的复位电路,如下图:

物联网-移远M26模块OpenCPU开发第1讲_第9张图片

使用外部自带看门狗功能的芯片进行复位的,必须给其喂狗,这个芯片才会正常工作,不输出复位信号。说明如下:

物联网-移远M26模块OpenCPU开发第1讲_第10张图片数据手册参见:SGM706-SYSB

从上图可知喂狗的引脚连接的是M26的16号引脚,通过引脚图可知是NETLIGHT引脚。如下图:

物联网-移远M26模块OpenCPU开发第1讲_第11张图片参见硬件设计手册:M26 OpenCPU硬件设计参考手册 

所以就需要M26的NETLIGHT引脚不停的给复位芯片喂狗才行,即必须在1.6s内有一个电平变化。我想就是这个导致,此时M26模块内应该没有这个信号,通过测量,NETLIGHT引脚的确没有电平变化的信号。这就导致M26一直在复位,无法提供稳定的电源给其下载flash。

问题找到了,我就纳闷了,为啥要这么设计,问一下设计人员,得到的回复竟然是到时候M26芯片会固化程序进去,装上就可以正常使用了。暂时移远提供的芯片没有固化这个功能,我也是无语了,控制程序不是还得我们来设计吗,这样搞的话,测试得多麻烦。

啥也不说了,解决问题吧。既然是复位引起的,那就在下载时跳过它吧。通过分析原理图,发现这个芯片的复位引脚是通过控制一个三极管驱动两路MOS管来断M26模块供电电源来实现复位的,那就直接跳过这个三极管,让MOS管一直导通,也就是让Q3的G极一直为低电平即可。电路如下:

物联网-移远M26模块OpenCPU开发第1讲_第12张图片          物联网-移远M26模块OpenCPU开发第1讲_第13张图片

我想那就直接焊接一根杜邦线,下载时短接GND应该就可以了吧。焊接好后立马测试一下,果然可以了。就是下载时比较麻烦,而且速度老慢了,下载内核耗时400多秒,不知是不是就是这样,还是电路的问题。

到此,重新下载内核后,就需要下载运行程序(含喂狗功能)进去看一下效果了,原来第一次上电时的指示灯和蜂鸣器工作是M26不停复位导致,我还以为是M26自带的测试代码,大写的尴尬。下载测试代码方法参考如下文档,里面有详细操作步骤:Quectel_QFlash_OpenCPU_User_Guide_V1.0。

至于如何编写程序,以及如何编程环境搭建,参考如下文档:

编程环境安装与配置:Quectel_OpenCPU_GCC_Installation_Guide_V1.1

编写程序快速入门:Quectel_OpenCPU_Quick_Start_Application_Note_V1.1

开发工具包:M26_OpenCPU_GS3_SDK_V2.0

编程参考文档:M26 OpenCPU用户指导手册

测试代码主要实现如下功能:

1.给外部复位芯片喂狗;

2.控制板子上的WORK指示灯闪烁,频率为1S;

3.串口打印相关信息。

下面我们来实现这些功能,需要给外部复位芯片喂狗,就得配置相关引脚为输出功能,初始化代码如下:

由于喂狗是周期性的,所以需要定时器功能,实现500ms定时器中断,方法如下:

物联网-移远M26模块OpenCPU开发第1讲_第14张图片

物联网-移远M26模块OpenCPU开发第1讲_第15张图片

实现work指示灯闪烁,图纸如下:

物联网-移远M26模块OpenCPU开发第1讲_第16张图片

32号引脚是PCM_IN,初始化代码如下:

需要实现闪烁,正好可以再定时器回调函数里实现。如下:

物联网-移远M26模块OpenCPU开发第1讲_第17张图片

因为板子上只使用了M26的主串口,所以此功能实现如下:

物联网-移远M26模块OpenCPU开发第1讲_第18张图片

编程详请参考相关手册,完整代码如下:

/*****************************************************************************
*  Copyright Statement:
*  --------------------
*  This software is protected by Copyright and the information contained
*  herein is confidential. The software may not be copied and the information
*  contained herein may not be used or disclosed except with the written
*  permission of Quectel Co., Ltd. 2013
*
*****************************************************************************/
/*****************************************************************************
 *
 * Filename:
 * ---------
 *   main.c
 *
 * Project:
 * --------
 *   OpenCPU
 *
 * Description:
 * ------------
 *
 *   测试代码
 *
 * Usage:
 * ------
 *   Compile & Run:
 *
 *     Set "C_PREDEF=-D __CUSTOMER_CODE__" in gcc_makefile file. And compile the 
 *     app using "make clean/new".
 *     Download image bin to module to run.
 *
 * Author: 顾小豆
 * 
 *
 *============================================================================
 *             HISTORY
 *----------------------------------------------------------------------------
 * 
 ****************************************************************************/
#ifdef __CUSTOMER_CODE__
#include "ql_trace.h"
#include "ql_system.h"
#include "ql_gpio.h"
#include "ql_stdlib.h"
#include "ql_error.h"
#include "ql_uart.h"
#include "ql_timer.h"
#include "ql_type.h"
#include "ql_error.h"

// 串口輸出配置,串口1
#define DEBUG_ENABLE 1
#if DEBUG_ENABLE > 0
#define DEBUG_PORT  UART_PORT1
#define DBG_BUF_LEN   512
static char DBG_BUFFER[DBG_BUF_LEN];
#define APP_DEBUG(FORMAT,...) {\
    Ql_memset(DBG_BUFFER, 0, DBG_BUF_LEN);\
    Ql_sprintf(DBG_BUFFER,FORMAT,##__VA_ARGS__); \
    if (UART_PORT2 == (DEBUG_PORT)) \
    {\
        Ql_Debug_Trace(DBG_BUFFER);\
    } else {\
        Ql_UART_Write((Enum_SerialPort)(DEBUG_PORT), (u8*)(DBG_BUFFER), Ql_strlen((const char *)(DBG_BUFFER)));\
    }\
}
#else
#define APP_DEBUG(FORMAT,...) 
#endif

// 定义控制引脚相关变量
static Enum_PinName g_wtd_gpioPin = PINNAME_NETLIGHT;     // 外部看门狗控制引脚
static Enum_PinName g_work_gpioPin = PINNAME_PCM_IN;      // WORK指示灯控制引脚

// 定义定时器相关变量
static u32 m_myTimerId = 2014;                            // 定时器ID
static u32 m_nInterval = 500;                             // 500ms中断
static void Callback_OnTimer(u32 timerId, void* param);   // 定时器回调函数

// 定义串口使用变量
static u8 m_Read_Buffer[1024];                            // 串口接收缓存
// 串口回调函数
static void CallBack_UART_Hdlr(Enum_SerialPort port, Enum_UARTEventType msg, bool level, void* customizedPara);

//  GPIO引脚初始化函数
static void GPIO_Program(void)
{    
    // 初始化NET指示灯引脚,低电平,无上下拉配置    
    Ql_GPIO_Init(g_net_gpioPin, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_DISABLE);  
		Ql_GPIO_SetLevel(g_net_gpioPin, PINLEVEL_LOW);        // 设定NET引脚为低电平
    
    // 初始化外部看门狗引脚,低电平,无上下拉配置
    Ql_GPIO_Init(g_wtd_gpioPin, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_DISABLE);   
    
    // 初始化外WORK指示灯引脚,低电平,无上下拉配置
    Ql_GPIO_Init(g_work_gpioPin, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_DISABLE);    
		Ql_GPIO_SetLevel(g_work_gpioPin, PINLEVEL_LOW);        // 设定WORK引脚为低电平
    
    // 初始化蜂鸣器引脚,低电平,无上下拉配置
    Ql_GPIO_Init(g_beep_gpioPin, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_DISABLE);
		Ql_GPIO_SetLevel(g_beep_gpioPin, PINLEVEL_LOW);        // 设定蜂鸣器引脚为低电平
    
    // 初始化输出端口引脚,低电平,无上下拉配置
    Ql_GPIO_Init(g_out_gpioPin, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_DISABLE);
		Ql_GPIO_SetLevel(g_out_gpioPin, PINLEVEL_LOW);        // 设定输出端口引脚为低电平

    
    // 睡眠 500ms,点亮NET指示灯.通过串口打印相关信息
    Ql_Sleep(500);
    Ql_GPIO_SetLevel(g_net_gpioPin, PINLEVEL_HIGH);
    APP_DEBUG("<-- 获取NET控制引脚电平: %d -->\r\n", Ql_GPIO_GetLevel(g_net_gpioPin));
    APP_DEBUG("<-- 点亮NET指示灯 -->\r\n");
}

// 定时器配置函数
static void TIMER_Program(void)
{    
	  // 注册定时器2014及定时器回调函数
		Ql_Timer_Register(m_myTimerId, Callback_OnTimer, NULL);
		// 开始定时器2014
		Ql_Timer_Start(m_myTimerId, m_nInterval, TRUE);
}

// 串口初始化函数
static void UART_Program(void)
{
    s32 ret;
    // 注册串口1和串口回调函数
    ret = Ql_UART_Register(UART_PORT1, CallBack_UART_Hdlr, NULL);
    // 注册失败打印相关信息
    if (ret < QL_RET_OK)
    {
        APP_DEBUG("Fail to register serial port[%d], ret=%d\r\n", UART_PORT1, ret);
    }
    // 打开串口1 波特率115200 无流控
    ret = Ql_UART_Open(UART_PORT1, 115200, FC_NONE);
    // 打开失败打印相关信息
    if (ret < QL_RET_OK)
    {
        APP_DEBUG("Fail to open serial port[%d], ret=%d\r\n", UART_PORT1, ret);
    }
}

// 喂狗函数
static void feed_wtd(void)
{
		s32 gpioLvl = Ql_GPIO_GetLevel(g_wtd_gpioPin);
		if (PINLEVEL_LOW == gpioLvl)
		{
				// 置位看门狗控制引脚
				Ql_GPIO_SetLevel(g_wtd_gpioPin, PINLEVEL_HIGH);
				APP_DEBUG("<-- 外部看门狗喂狗 -->\r\n");
		}
		else
		{
				// 复位看门狗控制引脚
				Ql_GPIO_SetLevel(g_wtd_gpioPin, PINLEVEL_LOW);
				APP_DEBUG("<-- 外部看门狗喂狗 -->\r\n");
		}	
}

// 蜂鸣器控制函数
static void beep_ctrl(void)
{
		
}
/************************************************************************/
/* The entrance for this example application                            */
/************************************************************************/
void proc_main_task(s32 taskId)
{
    ST_MSG msg;
    float g_f;

    
UART_Program();             // 调用串口初始化函数
    GPIO_Program();             // 调用GPIO初始化函数
    TIMER_Program();            // 调用定时器配置函数

    // Start message loop of this task
    while (TRUE)
    {  	  	
        Ql_OS_GetMessage(&msg);
        switch(msg.message)
        {
        case MSG_ID_USER_START:
            break;
        default:
            break;
        }
    }
}

static void Callback_OnTimer(u32 timerId, void* param)
{
	  static u16 timerCnt[10] = {0};
	  	   	
  	feed_wtd();
  	  	  	  	
  	if(timerCnt[0]++ >= 2)
		{
				s32 gpioLvl = Ql_GPIO_GetLevel(g_work_gpioPin);
				if (PINLEVEL_LOW == gpioLvl)
				{
				// 点亮work指示灯
				Ql_GPIO_SetLevel(g_work_gpioPin, PINLEVEL_HIGH);
				APP_DEBUG("<-- 点亮work指示灯 -->\r\n");
				}
				else
				{
				// 熄灭work指示灯
				Ql_GPIO_SetLevel(g_work_gpioPin, PINLEVEL_LOW);
				APP_DEBUG("<-- 关闭work指示灯 -->\r\n");
				}
				timerCnt[0] = 0;  			
		}
}	 

#endif //__EXAMPLE_GPIO__

欢迎读者提出疑问,可以加群讨论:838839442   微信公众号:物联网技术交流与学习

你可能感兴趣的:(物联网,物联网,移远,M26,OpenCPU)