×

LPC845 GPS寻路器开源分享

消耗积分:2 | 格式:zip | 大小:0.21 MB | 2023-01-03

陈利妮

分享资料个

描述

该项目使用开源minmea NMEA 解析器和低成本LPC845-BRK 开发板作为简单的位置查​​找器。

MCU 将等待来自通过 UART1 连接的板外 GPS(产品链接*)的初始锁定。一旦建立锁定(通过FIX引脚检测),当前位置将不断更新。然后您可以定期检查当前位置和定义的目标位置之间的距离。

可以在 UART0 上看到调试输出,它可通过 LPC845-BRK 上的 USB CDC 获得,从而在对源代码进行任何修改时轻松获得状态更新。

* 任何通过 UART 提供标准 NMEA 语句的 GPS 都应该在这个项目中工作。

 
pYYBAGOzheCAHQZUAAY-ZJOjUO4843.png
 

引出线

以下引脚用于连接外部 GPS(3 引脚)和 PWM 压电蜂鸣器(1 引脚):

  • 压电蜂鸣器的 SCT_OUT2(PWM 输出):P0.29
  • USART1_RXD(GPS):P0.26
  • USART1_TXD(GPS):P0.27
  • GPS 定位:P0.28

 

 
pYYBAGOzheOAArxLAAHDQG9Qpqo091.png
 

操作(调试模式)

当您将固件闪存到 LPC845(使用免费的跨平台 MCUXpresso IDE 和 LPC845-BRK 上的调试器)时,您可以使用任何终端仿真器包以9600bps连接到 USART1

退出重置后,您将看到以下消息:

LPC845 GPS Wayfinder
Waiting for a fix on the GPS module.
The GREEN LED indicates that we are waiting for a fix.
The BLUE LED indicates that we are parsing GPS data.

此时,等待锁定 3 颗或更多卫星时,LED 上的绿色 LED 将以 1Hz 的频率闪烁,这是位置锁定所需的。

一旦获得锁定,蓝色 LED 将开始闪烁,表明 MCU 当前正在处理来自 GPS 单元的 NMEA 数据,调试输出将更新为以下(示例)输出:

Current degree coordinates and speed: xx.xxxxxx, xx.xxxxxx (0.002333)
Distance to target: 14.30 km.

源代码

虽然大部分源代码相对容易理解,但下面描述了一些关键概念。

半正弦距离估计器

我们可以使用Haversine 公式的实现来确定两点之间的距离,如下所示:

float
calc_distance(struct gps_coord_fp_deg *a, struct gps_coord_fp_deg *b)
{
  float hav_r_meters = 6371e3; /* Mean radius of the earth in meters. */
  //float hav_r_miles = 3961;  /* Mean radius of the earth in miles. */
  /* Convert degrees to radians and calculate the deltas. */
  float lat1 = deg_to_rad(a->latitude);
  float lat2 = deg_to_rad(b->latitude);
  float lon1 = deg_to_rad(a->longitude);
  float lon2 = deg_to_rad(b->longitude);
  float delta_lat = lat2 - lat1;
  float delta_long = lon2 - lon1;
  /* Haversine */
  float hav_a = pow(sin(delta_lat/2.0f),2.0f) + cos(lat1) * cos(lat2) * pow(sin(delta_long/2.0f),2.0f);
  float hav_c = 2.0f * atan2(sqrt(hav_a), sqrt(1.0f-hav_a));
  /* Calculate the great circle distance in meters. */
  return hav_c * hav_r_meters;
}

Haversine 公式将近似计算两个位置之间球体表面的距离,输出以米为单位。您可以选择更新代码以通过调整函数顶部的系数来输出英里或任何其他单位。

Haversine 有点不完美,因为地球不是一个完美的球体,它以直线计算距离,但它是一个很好的整体距离近似值,计算量最少。

GPS 位置数据

应用程序中的另一个关键功能是解析RMC 句子的 NMEA 解析器(基于minmea ) (其他句子可以根据需要轻松添加!):

int
parse_nmea_sentence_release(char *line)
{
  switch (minmea_sentence_id(line, false))
  {
    case MINMEA_SENTENCE_RMC: {
      struct minmea_sentence_rmc frame;
      if (minmea_parse_rmc(&frame, line)) {
        g_gps_coord_fp_deg_last.latitude = minmea_tocoord(&frame.latitude);
        g_gps_coord_fp_deg_last.longitude = minmea_tocoord(&frame.longitude);
        g_gps_coord_fp_deg_last.speed = minmea_tocoord(&frame.speed);
        g_gps_coord_fp_deg_last.is_valid = true;
        PRINTF("Current degree coordinates and speed: %f, %f (%f)\r\n",
        g_gps_coord_fp_deg_last.latitude,
        g_gps_coord_fp_deg_last.longitude,
        g_gps_coord_fp_deg_last.speed);
        float dist_m = calc_distance(&g_gps_coord_fp_deg_last, &g_gps_coord_fp_deg_trgt);
        if (dist_m > 1000.0F) {
          /* Show distance in kilometers. */
          PRINTF("Distance to target: %.2f km.\r\n", dist_m / 1000.0F);
        } else {
          /* Show distance in meters */
          PRINTF("Distance to target: %.1f meters.\r\n", dist_m);
        }
      } else {
        PRINTF("$xxRMC sentence is not parsed\r\n");
      }
    } break;
    case MINMEA_INVALID: {
      /* $xxxxx sentence is not valid */
      error_blink();
    } break;
    default: {
      /* $xxxxx sentence is valid but wasn't handled above. */
    } break;
  }
  return 0;
}

在这个基本示例中,我们只解析RMC语句,它代表最小推荐数据,具有以下 NMEA 语句格式(来源):

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A

在哪里:

  • RMC - 推荐的最低刑期 C
  • 123519 - 修复时间为 12:35:19 UTC
  • A - 状态 A=active 或 V=Void。
  • 4807.038, N - 北纬 48 度 07.038'
  • 01131.000, E - 东经 11 度 31.000'
  • 022.4 - 对地速度(节)
  • 084.4 - 以度为单位的轨道角度 True
  • 230394 - 日期 - 1994 年 3 月 23 日
  • 003.1, W - 磁差
  • *6A - 校验和数据,始终以 * 开头

距离事件

这个应用程序的关键部分是上面 GPS 解析函数中的以下几行代码,每当解析引擎解析到新的位置定位时,它就会不断地将当前位置与目的地进行比较:

if (dist_m > 1000.0F) {
  /* Show distance in kilometers. */
  PRINTF("Distance to target: %.2f km.\r\n", dist_m / 1000.0F);
} else {
  /* Show distance in meters */
  PRINTF("Distance to target: %.1f meters.\r\n", dist_m);
}

根据你想做什么,你应该扩展上面的代码,比如当你接近目的地时,压电蜂鸣器发出越来越响亮或频率越来越高的声音。

压电蜂鸣器

根据您的应用要求,您可以选择使用以下宏启用或禁用压电蜂鸣器:

PIEZO_ON();
/* ... do something ... */
PIEZO_OFF();

可以使用main.c中的以下宏定义来调整压电蜂鸣器(使用 SCT 外围设备)发出的确切频率

#define SCTIMER_CLK_FREQ    (CLOCK_GetFreq(kCLOCK_Fro))
#define SCTIMER_OUT         (kSCTIMER_Out_2)
#define SCTIMER_PIEZO_FREQ  (4000U)
#define SCTIMER_PIEZO_DUTY  (25U)
#define PIEZO_ON()          SCTIMER_StartTimer(SCT0, kSCTIMER_Counter_L);
#define PIEZO_OFF()         SCTIMER_StopTimer(SCT0, kSCTIMER_Counter_L);

进一步发展

虽然当前的概念证明具有简单、便携式寻路器的关键要素,但我目前正在研究一种外壳,使整个设备便携,并由 3.7V LIPO 电池供电。当我完成一些原型时,硬件文件将在此处可用。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'LPC845 GPS寻路器开源分享',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);