精华内容
下载资源
问答
  • 平衡小车之家原创资料,有控制板原理图和各种版本的源代码,有控制板原理图和各种版本的源代码 平衡小车之家原创资料。
  • 平衡小车之家两轮平衡小车程序,包括arduino和STM32的程序及原理图,硬件参考资料,驱动和调试软件,开发环境软件和库文件,测速码盘电机详细讲解资料等,可以说是非常全面的
  • 平衡小车之家原版资料

    热门讨论 2017-02-07 15:50:47
    平衡小车之家原创资料,有控制板原理图和各种版本的源代码
  • pid小车源码。舵机转向,来自于平衡小车之家,基本没有什么bug
  • 32F1旋转倒立摆 倾角环+位置环....平衡小车之家源码
  • 平衡小车源码 平衡小车之家 Next year marks the 50th anniversary of one of the world’s most famous ‘thought experiments’. It may lack the pop-cultural punch of ‘Schrödinger’s famous cat‘, but it...

    平衡小车源码 平衡小车之家

    Next year marks the 50th anniversary of one of the world’s most famous ‘thought experiments’. It may lack the pop-cultural punch of ‘Schrödinger’s famous cat‘, but it still stands out as an absolute zinger – it’s called ‘The Trolley Problem‘.

    明年是世界上最著名的“思想实验”之一的50周年。 它可能缺少“ 薛定ding的著名猫 ”的流行文化冲击,但它仍然是绝对的歌手-它被称为“ 手推车问题 ”。

    Vintage photo of a railway signalman.

    Phillipa Foote first sketched out the idea in 1967 and it was a philosophical question designed to probe at the soft underbelly of your ethics. Many subtle variations have sprung up since, but the core scenario goes like this:

    菲利普·富特(Phillipa Foote)于1967年首先提出了这个想法,这是一个旨在探究您的道德软肋的哲学问题。 此后,出现了许多细微的变化,但是核心场景是这样的:

    1. Imagine a runaway trolley car (or train) is careering down a track towards a group of five workers. You have no way to warn them and they will most certainly all die without some kind of intervention.

      想象一下,一辆失控的无轨电车(或火车)正朝着五个工人的方向前进。 您没有办法警告他们,并且如果没有某种干预,他们肯定会全部死亡。
    2. You have control of a switch lever that can divert the trolley into a side-track, instantly saving our five workers. However, there’s another person standing on that side-track who will now have no opportunity to escape a ‘hurtling trolley death’.

      您可以控制切换杆,该切换杆可以将手推车转移到侧轨,立即节省了我们的五名工人。 但是,有另一个人站在那条旁道上,现在将没有机会逃脱“令人费解的电车死亡”。
    Diagram: The Trolley Car Problem. Illustration - Alex Walker

    你会怎么做? ( What Would You Do?)

    • Do nothing, witness five deaths, but bear no direct responsibility for the loss of life.

      无所作为,目睹五人死亡,但对生命损失不承担直接责任。

    • Or take action, save five lives, but personally bring about the death of another human.

      或采取行动,挽救五个生命,但亲自导致另一个人的死亡。

    It’s a tough problem and there’s no easy or unequivocally right answer. My daughter wanted to shout at them all to move but that’s not an option.

    这是一个棘手的问题,没有简单或明确正确的答案。 我的女儿想大声喊叫他们动弹,但这不是一个选择。

    For what it’s worth, most people choose to sacrifice the single person. The greater good.

    为了它的价值, 大多数人选择牺牲一个人。 更大的好处。

    However, the most common variation introduces a new person and is rather uncharitably called ‘The Fat Man’ scenario. In this story, you can choose to save the workers by – as nasty as it might sound – pushing a rather generously proportioned man in front of the oncoming trolley.

    但是,最常见的变体引入了一个新人,因此被称为“胖子”场景。 在这个故事中,您可以选择挽救工人-听起来很讨厌-将一个相当慷慨的男人推到迎面而来的手推车前。

    Unsurprisingly, most people are appalled by this idea. Regardless of the upside, surely killing an innocent man by pushing him in front of a train is the act of a monster?!

    毫不奇怪,大多数人对此想法感到震惊。 不管有什么好处,一定要把一个无辜的人推到火车前杀死他,这是怪物的举动吗?

    Of course, in a strictly mathematical sense, the only difference between the two stories above, is the method by which you choose to dispatch the unfortunate person. Levers are certainly much cleaner than a well-timed elbow. Does the method matter or is it all about the outcome?

    当然,从严格的数学意义上讲,上述两个故事之间的唯一区别是您选择派遣不幸人员的方法。 拉杆肯定比适时的肘部清洁得多。 方法是重要的还是全部有关结果?

    If you’re having trouble getting your head around the idea, Harry Shearer – he of Simpson’s and Spinal Tap fame – made a great video for BBC Radio 4 in 2014 that explains the problem brilliantly.

    如果您难以理解这个想法,那么辛普森(Simpson)和《脊髓轻击》(Spinal Tap)的创始人哈里·希勒(Harry Shearer)在2014年为BBC Radio 4制作了一段精彩的视频,很好地解释了这个问题。

    幸运的是,思想实验是无害的,对吗? ( Lucky That Thought Experiments are Harmless, Right?)

    Of course, this is all just theory. Brain games. A mischievous pub conversation or, at worst, a chance for philosophy nerds to show off at dinner parties.

    当然,这仅仅是理论上的。 脑游戏。 恶作剧的酒吧谈话,或者更糟的是,哲学家很可能在晚宴上炫耀。

    Not quite.

    不完全的。

    Google self-driving car.

    Google self-driving car.

    Google自动驾驶汽车。

    As we’re all aware, most of the world’s major car companies are investing in driverless technology. We know these systems are already safer that the average human driver. Unlike us, robot cars are built to scan surrounding traffic thousands of times every second and instantly adjust to changes.

    众所周知,世界上大多数大型汽车公司都在投资无人驾驶技术。 我们知道这些系统已经比普通驾驶员更安全。 与我们不同,机器人汽车每秒可扫描数千次周围交通,并即时适应变化。

    But they can’t predict everything. Tires fail catastrophically. Trees fall unpredictably. Drivers suffer seizures. Very occasionally driverless cars are going to be in their own ‘Trolley Car’ scenarios. And – presumably – software developers are currently writing the decision algorithms to handle them. Set a ‘0’ and we go straight – set a ‘1’ and we turn.

    但是他们无法预测一切 。 轮胎发生灾难性故障。 树木意外地倒下。 驾驶员会癫痫发作。 无人驾驶汽车偶尔会处于自己的“无轨电车”场景中。 而且( 大概 )软件开发人员目前正在编写决策算法来处理它们。 设置为“ 0”,我们直走–设置为“ 1”,我们转弯

    It raises some heavy moral questions.

    它提出了一些沉重的道德问题。

    • If you paid for a car, would you expect it to prioritize your safety over others?

      如果您购买了汽车,您是否希望它比其他人优先考虑您的安全?
    • Is it a pure numbers game of ‘save the most humans’?

      它是“拯救人类最多”的纯粹数字游戏吗?
    • Should the age of the people make a difference? Perhaps babies get preference?

      人民的年龄应该有所作为吗? 也许婴儿会喜欢?
    • Should the car be taking into account the estimated body weights of potential accident victims?

      该车是否应考虑潜在事故受害者的估计体重?
    • Should prestige cars make different decisions to economy cars?

      高档汽车是否应与经济型汽车做出不同的决定?
    • Would Donald Trump’s car accelerate? (jk)

      唐纳德·特朗普的汽车会加速吗? (jk)

    Are software engineers even equipped to take on these kinds of tricky ethical questions? It’s certainly not part of most computer science courses. Perhaps Google, Volvo, and Ford need to be hiring more philosophers and ethicists.

    软件工程师是否有能力解决这些棘手的道德问题? 当然,这不是大多数计算机科学课程的一部分。 也许Google,沃尔沃和福特需要雇用更多的哲学家和伦理学家。

    With currently only a few hundred self-driving cars on the planet, these questions still falls close to the realm of the ‘thought experiment’.

    目前地球上只有几百辆自动驾驶汽车,这些问题仍然接近 “思想实验”的领域。

    But 10 years from now there could be millions of self-driving cars making life or death decisions every day. Things are about to get ‘real’– fast.

    但是从现在开始的10年后,每天都会有数百万辆无人驾驶汽车做出生死攸关的决定。 事情将很快变得“真实”。

    May you live in interesting times.

    愿您生活在有趣的时代。

    翻译自: https://www.sitepoint.com/the-trolley-problem-will-our-robot-cars-grow-up-to-be-heroes/

    平衡小车源码 平衡小车之家

    展开全文
  • 平衡小车之家客服真差

    千次阅读 热门讨论 2019-04-23 11:32:19
    都是在平衡小车之家买的,好好看看下面的图片。 最近在研究同事的步进平衡小车,然后跑去问一下客服步进电机的参数,一看我说,同事之前买过小车了,就不理人了, 简直是无耻商家啊。 要说我是虚构也没...

      我同事送了我一台直流电机平衡车,然后同事又买了一台步进电机平衡车。都是在平衡小车之家买的,好好看看下面的图片。

    最近在研究同事的步进平衡小车,然后跑去问一下客服步进电机的参数,一看我说,同事之前买过小车了,就不理人了,

    简直是无耻商家啊。

    要说我是虚构也没关系,明人不做暗事,我和你又没有利益关系,我没必要黑你,

    我既然有你家的产品你就应该服务我,而不是我已经有了你家产品,觉得我不会买了,就不了了之。

    展开全文
  • 使用平衡小车之家的最小系统板,配合TB6612驱动四路电机,只是自己记录一下,我知道我很垃圾。
  • 本资料包括了全部的平衡小车资料,应有尽有,可以很快的开发属于自己的平衡小车。资料很全,而且还包括了自己找的资料,都是开发平衡小车用的,很实用
  • 平衡小车之家资料.zip

    2020-04-20 17:26:33
    里面包含资料、文档、开发源码、开发视频、开发笔记等。完整版平衡小车资料以及开发过程。还有原理图、使用说明等等
  • 平衡小车之家--风力摆开箱实录[附资料]前言硬件部分相关资料 前言 你好学弟!这里是学长向你展示他们在2019年夏天花了大价钱购入一台平衡小车之家的风力摆,一直想照着搭一个,结果刚研究完就比赛了。 老师让我们把...

    平衡小车之家--风力摆开箱实录[附资料]

    前言

    你好学弟!这里是学长向你展示他们在2019年夏天花了大价钱购入一台平衡小车之家的风力摆,一直想照着搭一个,结果刚研究完就比赛了。
    老师让我们把这堆破烂留给你们(有些零件缺了你们要自己买 -_-),这边怕你们不懂,完全自愿的给你们写了一篇教程,规避一些我们走过的弯路,也希望你们能把这份精神传承下去,替下下届的接班人们开山铺路。话不多说,先上个实物图。
    在这里插入图片描述
    还有一个电源:
    在这里插入图片描述

    硬件部分

    在这里插入图片描述
    这根杆是非常软质的那种,很有弹性,我们之前买过一根碳纤维杆完全不适合。
    其次它所有重量全都集中在底部,惯性相当好。总共分三层,下面一一拆解开来看一下:

    • 主控层:
      主控为一块F103,左边5个排母是OLED接口,如果插上的话可以看到输出的调试信息。右边有四根排针为SWD调试接口,可以用J-link调试器进行调试。下面是一个串口IO,可以通过串口查看信息。

    在这里插入图片描述

    • 硬件层:
      这一层有一块MPU6050用于姿态测量,一块蓝牙模块用于使用配套的APP,还有从顶部接入的主电源。输出方面有四路电机插槽,不知道是怎么驱动空心杯电机的,因为并没有看到任何驱动器件,还有一个激光头输出。
      在这里插入图片描述

    • 机械层:
      最底层是四路空心杯电机和一个激光头用于指示路径。
      在这里插入图片描述

    代码部分

    用Keil v5建的工程,用Keil v4要装一个Keil v5,而且Keil v4要卸载掉,不然会起冲突。
    先看主函数部分:

    int main(void)
      { 
    	  MY_NVIC_PriorityGroupConfig(2); //=====设置中断分组
    	  uart1_init(128000);             //=====串口1初始化
    	  uart2_init(9600);               //=====串口2初始化 
    	  delay_init();	    	          //=====延时函数初始化
    //    JTAG_Set(JTAG_SWD_DISABLE);     //=====关闭JTAG接口
    //	  JTAG_Set(SWD_ENABLE);           //=====打开SWD接口
    	  LED_Init();                     //=====初始化LED
    	  KEY_Init();                     //=====按键初始化
          MiniBalance_PWM_Init(7199,0);   //=====初始化PWM10KHZ,用于驱动电机
    	  OLED_Init();                    //=====OLED初始化	    
    	  delay_ms(1000);                 //=====延时
          
          IIC_Init();                     //=====IIC初始化
          MPU6050_initialize();           //=====MPU6050初始化	
      	  DMP_Init();                     //=====初始化DMP   
          Adc_Init();                     //=====adc初始化	
          MiniBalance_EXTI_Init();        //=====MPU6050 5ms定时中断初始化  200Hz定时
    	  Laser=1;                        //=====开启激光灯  PA12
    	  if(KEY==0)Flash_Read();         //=====开机长按用户按键读取flash参数运行
        while(1)
    	   {
    		   if(Debug_key==7&&Flag_Zero)  {Read_AngleZero(Flag_Zero,10);}//调试界面按键7(第三排第一个)零点读取
    		   if(Debug_key==4&&Flag_Zero==0) {Flag_Zero=1,count_Zero=0,LED=0;}//调试界面按键4(第二排第一个)零点读取标志位,建议4,7重复两次
    		   if(Flag_Show==0)           //默认开启APP和OLED显示屏,长安按键3S切换上位机
    		   {
    			   APP_Show();	          // APP显示数据
    			   oled_show();           //===显示屏打开
    		   }
    		   else DataScope();          //开启上位机
               
    		   if(Flash_Send==1)          //写入PID参数到Flash,由app控制该指令
    		   {
    			   Flash_WritePID();      //将PID参数写入Flash保存
    			   Flash_Send=0; 
    		   }
               delay_flag=1;	      
               delay_50=0;
               while(delay_flag);  //通过MPU6050的INT中断实现精准的50ms延时
    	  } 
    }
    

    其他的就大致讲解一下各个文件的内容:
    在这里插入图片描述

    main.c:
    包含全部变量的定义,初始化与主循环函数,所有文件通过调用sys.h来使用变量
    
    sys.c:
    包含所有系统相关初始化函数的定义,如时钟初始化,配置JTAG模式,配置外部中断等
    
    usart.c:
    包含uart1与uart2的初始化。uart2的中断处理函数,用于处理蓝牙传过来的数据。fputc函数的重定义,这里同时使用了串口12。
    
    LED.c:
    LED的初始化与闪烁函数
    
    key.c:
    按键的初始化与双击,单击,长按函数
    
    oled.c:
    oled的初始化与各种使用函数
    
    adc.c:
    ADC1的初始化与使用,用于获取电池电压
    
    motor.c:
    定义用于驱动四个电机的PWM,初始化TIM3的四个PWM输出脚
    
    IOI2C.c:
    用于定义一个模拟IIC协议,是给MPU6050使用的
    
    exti.c:
    将PB6定义为一个外部触发的中断,由MPU6050给出中断信号,得到一个精确的5ms时钟
    
    stmflash.c:
    对stm32的内部flash进行初始化与读写操作函数,主要用于存取预设的PID参数
    
    control.c:
    是整个工程的主要控制逻辑部分,包含
    ------------------------------------------------------------------------------------
    int EXTI9_5_IRQHandler(void) //由MPU6050触发的5ms定时中断处理函数
    //包括姿态更新Read_DMP(),控制模式更新Get_RC(),PID计算,按键扫描,电机的PWM赋值
    
    int Position_PID_X (float value,float Target)	//PID的的计算,这里采用的就是传统的PID,
    int Position_PID_Y (float value,float Target)
    //关于PID的相关说明与各种PID,见https://blog.csdn.net/foxclever/article/category/9281151,这是一个系列,建议从第一篇到结尾都看一遍
    
    void Get_RC(void)	//处理APP传入的控制模式信息
    //根据uart2中断里置位的各种标志位进行一系列的switch-case操作
    ------------------------------------------------------------------------------------
    
    inv_mpu.c:
    inv_mpu_dmp_motion_driver.c:
    MPU6050的dmp单元相关库,如果要使用MPU内置的dmp单元则需要包含
    
    MPU6050.c
    包含MPU6050的初始化与内部信息的读取函数
    
    filter.c	(未使用)
    定义了卡尔曼滤波与一阶滤波的相关函数
    
    show.c
    包含
    void oled_show(void)用于在oled显示屏上显示数据。
    void APP_Show(void)用于向串口2 APP发送数据。
    void DataScope(void)用于向串口1 上位机发送数据
    

    相关资料

    店家给的资料其实还挺多的,然而你不买根本不会给你。
    腾讯微云:https://share.weiyun.com/5kC2VAe 密码:bt5u5c
    源码链接:https://download.csdn.net/download/qq_43243338/11939899

    展开全文
  • stm32 平衡小车pid算法代码加教程
  • 为了更好地应用PS2遥控手柄,我想尽可能理解一下它与stm32单片机间通信控制的过程,首先看了平衡小车之家给的PS2遥控手柄使用说明,讲解的内容比较简洁,光凭这个说明不能很轻易地理解配套的程序逻辑,接下来结合...

    为了更好地应用PS2遥控手柄,我想尽可能理解一下它与stm32单片机间通信控制的过程,首先看了平衡小车之家给的PS2遥控手柄使用说明,讲解的内容比较简洁,光凭这个说明不能很轻易地理解配套的程序逻辑,接下来结合平衡小车之家的程序内容对照说明解释一下我的理解。因是个人理解并非官方说明,如有误请帮助指出改正,非常感谢!

    一、自己看一遍说明

    在看程序之前要先看一下说明里的介绍,大致了解一下。
    说明及测试源码:
    链接:https://pan.baidu.com/s/1hC4Gbjfh87vsswuJyUsH0g
    提取码:fdzf

    二、结合说明理解程序pstwo.c(.h)

    Tips:请按照顺序仔细阅读,前面介绍过的一些基础在后面其他函数中同样应用到时就不再赘述。

    1.定义的数组:

    Comd[2]={0x01,0x42} :存储了两条指令码,分别是开始指令和请求数据指令。
    Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} :数据存储数组,初始全为0,这是最重要的数组,功能在接下来的程序理解中慢慢介绍。
    MASK[16]={PSB_SELECT,PSB_L3,PSB_R3,PSB_START,PSB_PAD_UP,PSB_PAD_RIGHT,PSB_PAD_DOWN,PSB_PAD_LEFT,PSB_L2,PSB_R2,PSB_L1,PSB_R1 ,PSB_GREEN,PSB_RED,PSB_BLUE,PSB_PINK} :按键名字的数组,在宏定义中对这些按键赋予了从1~16的按键值。

    2.发送命令函数PS2_Cmd(u8 CMD)

    看完介绍接下来看程序内容,首先注意,DI与DO是一对同时传输的8 bit串行数据,所谓串行数据特点即按位(1 bit)传输,其次,CLK时钟信号下降沿时完成信号的发送与接收,根据这些,我们首先理解一下PS2_Cmd(u8 CMD)这个函数的意义:

    void PS2_Cmd(u8 CMD)
    {
     volatile u16 ref=0x01;
     Data[1] = 0;
     for(ref=0x01;ref<0x0100;ref<<=1)
     {
      if(ref&CMD)
      {
       DO_H;                   //输出一位控制位
      }
      else DO_L;
      CLK_H;                        //时钟拉高
      DELAY_TIME;
      CLK_L;
      DELAY_TIME;
      CLK_H;                        //手动拉出一个下降沿使DO和DI得以同时传送
      if(DI)
       Data[1] = ref|Data[1];      //运用或运算按位存入Data[1]的8位
     }
     delay_us(16);
    }

    Tips:volatile是易变型变量,是防止编译器优化代码时假设这个变量的值,保证每次小心地重新读取值。
    对于图中的for循环,可以得知ref的变化是一个八位二进制数中唯一一个1的位置变化,从最低位到最高位移动,从0000 0001到1000 0000。
    &按位与的操作,根据定义可以理解到ref&CMD得到的结果是:当ref中1的位置对应CMD中得位置上也为1时,结果为1;当ref中1的位置对应CMD中得位置上为0时,结果为0。CMD的其他位则不影响此结果。
    而这个结果为1时,DO_H即输出1,这个结果为0时,DO_L即输出0。因此for循环八次,DO的结果就是将CMD的每一位传送了过去。
    每次循环中下面这一段时钟信号拉高又拉低的操作,是为了手动置出一次下降沿,在这个下降沿中,DO信号才能得以发送,同时DI的信号得以接收回来。
    因此接下来又对接收到的DI进行判断:当DI为1时,运用按位或操作,根据Data[1]初始值为0000 0000,以及按位或的定义,不难理解ref | Data[1]得到新的Data[1]的过程是:ref里的唯一的1以值不变位置不变的形式给到结果的二进制数中,比如某一次循环Data[1] = 0000 0010,ref = 0000 1000,且DI=1,则ref | Data[1]=0000 1010。
    而这个给1的操作,只有这一bit的DI=1时才会进行;若DI=0,则ref只进行1的移位,不给予,但其实也就相当于这一位ref是给予了0给Data[1]。所以其实判断DI并执行从句的这一步在整个for循环后的结果即是将8 bit的DI按位保存到Data[1]。
    至此,可以说理解了这个发送命令的函数的逻辑组成,总结到它的功能就是:每执行一次该函数,就将参数CMD以八位二进制按位发送给手柄,同时从手柄接收信号以八位二进制按位返回给单片机并存储到Data[1]。

    2.读取手柄数据函数PS2_ReadData(void)

    对于从手柄返回给单片机的数据,最重要的应该是按键和摇杆当前的状态数据,有了这个数据才能用程序处理判断当前用户的动作,再根据按键功能执行相应的操作程序。前面说到,Data[1]已经用来存储每次执行PS2_Cmd函数时DI返回的信号数据,那么Data数组其余的7个位置存储的就应该是需要返回给单片机进行程序处理的有效数据了。
    首先要注意,数据的通讯传输必须在CS拉低期间进行,所以即使有了发送命令函数,在执行这个函数前也要先拉低CS,即如图程序中开头部分的CS_L,而在通讯结束、数据传输完成后,还要将CS拉回高电平,以便下一次的通讯,也就是这个函数的结尾的CS_H。

    //判断是否为红灯模式,0x41=模拟绿灯,0x73=模拟红灯
    //返回值;0,红灯模式
    //    其他,其他模式
    void PS2_ReadData(void)
    {
     volatile u8 byte=0;
     volatile u16 ref=0x01;
     CS_L;
     PS2_Cmd(Comd[0]);  //发送开始命令0X01
     PS2_Cmd(Comd[1]);  //发送请求数据命令0X42
     for(byte=2;byte<9;byte++)          //开始接受数据
     {
      for(ref=0x01;ref<0x100;ref<<=1)
       {
         CLK_H;
         DELAY_TIME;
         CLK_L;
         DELAY_TIME;
         CLK_H;
            if(DI)
            Data[byte] = ref|Data[byte];  
        }
            delay_us(16);
     }
     CS_H;
    }

    接下来注意到程序向手柄发送了两条命令,这两条命令都来自于之前定义的Comd[2]数组,因此接下来要知道,想要让手柄返回有效的按键状态数据给单片机,要先发送开始命令0x01和请求数据命令0x42,而且紧接着,手柄将会返回一个数据0x5A给单片机,意味着已经接收到请求,即将返回数据。再接下来,就是返回各按键以及摇杆的状态数据了。说明中数据意义对照表如下:

    在这里插入图片描述
    Idle代表这时此时该数据线上无含有效意义的数据传送。这张表乍一看并不太能明白,但至少前三行的三个十六进制数的含义我们已经了解了。
    回到PS2_ReadData这个函数的代码中继续看,接下来的一部分和PS2_Cmd中非常类似,不难理解,这一段的意义即为:内层循环结束后即将DI返回的八位二进制数据按位存储到了Data数组中的某一个元素位置,而外层循环则是将数据依次存储从Data[2]到Data[8]的位置。
    到这里我才意识到两个函数中各个用到delay的意义,结合时序图其实很好理解,关于CLK拉低又拉高期间DELAY_TIME是CLK时钟信号频率的需求,说明中提到,如果数据接收不稳定,可以适当增加频率;而for循环结束后的delay_us应该是因为要等待DI和DO数据的发送与接收完成。
    这个函数功能总结为:发送开始命令和请求数据命令,然后接收到返回的预告,存入Data[2],紧接着接收到按键及摇杆当前的状态数据,并存储到Data[3]到Data[8]这七个元素位置。
    至此,发送命令与接收数据函数都得以理解。

    3.判断模式函数u8 PS2_RedLight(void)

    //判断是否为红灯模式,0x41=模拟绿灯,0x73=模拟红灯
    //返回值;0,红灯模式
    //    其他,其他模式
    u8 PS2_RedLight(void)
    {
     CS_L;
     PS2_Cmd(Comd[0]);  //发送开始命令0X01
     PS2_Cmd(Comd[1]);  //发送请求数据命令0X42
     CS_H;
     if( Data[1] == 0X73)   return 0 ;
     else return 1;
    }

    这个函数很简单,就是如数据意义对照表中1行,DO发送0X42同时DI返回ID,这个ID也是一个十六进制数,这个函数就是判断这个ID是什么,若是0x73,则为红灯模式,该函数返回值为0;若是其他值,则函数返回值为1。至于模式的设置我们接下来会再介绍。
    注意这里判断的是Data[1],这是因为这个ID是在DO发送0X42同时DI返回的值,按照PS2_Cmd的意义,应当是存储在Data[1]里的,而不是其他元素位置。

    4.清除数据缓冲区PS2_ClearData()

    //清除数据缓冲区
    void PS2_ClearData(void)
    {
     u8 a;
     for(a=0;a<9;a++)
      Data[a]=0x00;
    }

    相信这个无需解释,就是清除之前缓冲存储在其中的数据,将Data数组中的元素全部归零,以便下次使用。

    5.分析返回数据以判断哪一个按键按下u8 PS2_DataKey()

    u8 PS2_DataKey()
    {
     u8 index;
     PS2_ClearData();
     PS2_ReadData();
      Handkey=(Data[4]<<8)|Data[3];     //这是16个按键  按下为0, 未按下为1
      for(index=0;index<16;index++)
       {     
         if((Handkey&(1<<(MASK[index]-1)))==0)
         return index+1;
       }
     return 0;          //没有任何按键按下
    }

    手柄上的按键共有16个,接收到当前按键状态的数据,是以两个八位二进制数也就是两个元素存储在Data数组里的,根据读数据的函数以及数据意义对照表可以知道,即是Data[3]和Data[4],共16 bit,每一位存储一个按键当前的状态值,按键按下为0,未按为1。
    Handkey在程序一开始进行了定义,是一个u16的变量,因此是16位二进制数,Data[4]<<8这一步的结果即是高8位为原Data[4],低8位为0000 0000 ,结果再与Data[3]进行按位或,得到的结果应是高8位为原Data[4],低8位为原Data[3],将这个结果赋给Handkey,则这个16位二进制数里就包含了所有的键状态值。
    接下来的for循环是检测哪一个按键被按下的最重要的部分:
    MASK[index]取出数组中的键值,再减一,得到的结果作为一个移位的位数X,1<<(MASK[index]-1)即让0000 0000 0000 0001中唯一的1左移这个位数X,因为每个键的键值都是它在数组中的序号加一,所以0000 0000 0000 0001移位后得到的结果中唯一的1所在的位置刚好是取出的那个键在数组中的位置(序号),移位后的结果与Handkey进行按位与,逻辑结果为:1<<(MASK[index]-1)的结果中应只有一个位置上值是1,则只有Handkey中对应同样位置上值是0时,这二者按位与的结果才为0。Handkey的其他位上值是什么不影响这个结果。
    只有当结果为0时,index+1并作为函数返回值,则这个index+1就是键值。

    这一段如果难以理解,最简便的办法就是index取一个值,走一遍程序,就能理解了。
    这个循环执行16次,即将Handkey的每一位都进行检测,检测出按键状态值为0就立即返回这个键的键值,并且结束整个函数(return的作用)。
    循环结束后还没有return值的话就说明没有按键按下,则return 0。
    注意,开头的PS2_ClearData();再PS2_ReadData();是必要的,这是保证现在数组里存的是当前立即更新的键值。
    注意,这个函数只能检测一个按键被按下,若同时按多个按键,则只能检测到键值最小的那个,因此如果有兴趣还可以自己写一个组合按键的函数,能实现更多功能。

    6.得到摇杆的状态数据u8 PS2_AnologData(u8 button)

    
    //得到一个摇杆的模拟量  范围0~256
    u8 PS2_AnologData(u8 button)
    {
     return Data[button];
    }

    根据数据意义对照表,Data[5]到Data[8]存储的是摇杆的状态数据,分为左/右摇杆的X/Y轴向值,共四个值,当摇杆向X/Y轴推动时,不同的位置会有不同的数值,每个轴向值范围都是0~256,0x00为最左或最上,0xff为最右或最下。应用时根据入口参数button的值返回Data数组相应位置序号里存储的状态数,因此在头文件中也宏定义了四个值对应的数组位置序号值5/6/7/8。

    到这里我们可以引入我上网查的资料中所述所谓红灯模式与绿灯模式:
    红灯模式时:左右摇杆发送模拟值,0x00~0xFF 之间,且摇杆按下的键值值 L3、R3 有效;
    绿灯模式时:左右摇杆模拟值为无效,推到极限时,则对应发送为
    UP、RIGHT、DOWN、LEFT、△、○、╳、□,此时按键 L3、R3 无效。

    因此如果想要进行一些流畅性的控制比如小车行驶等等,则使用红灯模式比较合适,我认为像变化较大的调参用摇杆也比较方便,而模式选择MODE键是否可用在下面的函数中也可以设置。

    7.其他函数

    剩下的函数主要都是靠在CS拉低期间发送各种指令码实现的,这里简单带过一下重点的三个函数:

    (1)手柄震动函数PS2_Vibration(u8 motor1, u8 motor2)

    为了游戏体验感比如赛车撞墙等等,手柄还加入了震动功能,主要由参数motor1和motor2来决定,motor1仅在为0x00时关右侧电机,其他值则开右侧电机并小幅震动,motor2的值则可从0x40~0xff,这时左侧电机震动,且motor2的值越大,震动越强。

    (2)发送模式设置PS2_TurnOnAnalogMode(void)

    //发送模式设置
    void PS2_TurnOnAnalogMode(void)
    {
     CS_L;
     PS2_Cmd(0x01);  
     PS2_Cmd(0x44);  
     PS2_Cmd(0X00);
     PS2_Cmd(0x01); //analog=0x01;digital=0x00  软件设置发送模式
     PS2_Cmd(0x03); //Ox03锁存设置,即不可通过按键“MODE”设置模式。
           //0xEE不锁存软件设置,可通过按键“MODE”设置模式。
     PS2_Cmd(0X00);
     PS2_Cmd(0X00);
     PS2_Cmd(0X00);
     PS2_Cmd(0X00);
     CS_H;
     delay_us(16);
    }

    这里的重点在于函数内的第5行和第6行:
    第5行语句实现软件设置发送模式,指令值为0x01则可发送摇杆模拟量,即红灯模式;指令值为0x00则为绿灯模式,不发送模拟量。
    第6行则是对于发送模式可不可以用MODE按键设置的指令,指令值为0X03则只可以通过第5行指令软件设置发送模式;指令值为0xEE则不锁存软件设置,可以通过按MODE键设置红灯/绿灯模式。

    (3)手柄配置初始化函数PS2_SetInit(void)

    包含完成各种配置函数及保存配置函数,其中原代码默认注释掉了震动模式的配置,可以自己开启。

    8.总结:

    在main.c中有测试代码,理解了以上函数后就很好理解了,同时也很方便自己改动设置按键功能了,虽然本篇理解有些冗长,不过在写这篇理解的过程中还是很有意思的,尤其对于按位与和按位或的逻辑功能,这辈子是忘不了了…希望本篇对于想用遥控手柄做一些控制的读者能有所帮助,再次希望如果理解有误能有大神指出,万分感谢!

    展开全文
  • 采用stm32单片机开发,两轮平衡小车,含全部源代码。含各种资料和论文,助你从零开始搭建平衡车系统。
  • pid 学习资料
  • 电赛倒立摆代码,有助于学习,代码很全,适合学习,能很好的帮助大家学习,通过对比发现这个资源很好,代码实用性强,点赛和小磊比赛都是用
  • 这里的全部源码是不会贴了,毕竟还要尊重一下平衡小车之家的知识产权,我这里主要把思路说清楚,具体的内容不会写太细 开始吧 这个图是平衡小车之家配套的app,用来控制底盘移动以及一些参数的调整。收发方式通过...
  • pid基础资料,平衡小车ppt全部资料,开发飞行器,平衡小车
  • 我现在发现平衡小车之家里的平衡车,角度环和速度环直接相加,在数学表达式上是和串级PID一样的!!!!!!!!! 这也是为什么它说没有直接使用串级PID,意思是间接使用了。 不过速度环变成正反馈,好好思考下。...
  • 首先跟大家介绍应用场景,我们使用 增量式速度PI 控制直流减速编码电机是在 参加 全国大学生工程训练综合能力竞赛时 为了控制一个麦克纳姆轮小车而准备的, 在应用需求中我们一共是需要控制四路 电机 分别对四个电机...
  • 概述: 根据控制方式舵机应该称为微型伺服马达。早期在模型上使用最多,主要用于控制模型的舵面,所以俗称舵机。舵机特点是接受一个简单的控制指令就可以自动转到一个比较精确的角度。 注: ...
  • 作者:平衡小车之家 我的淘宝小店:http://shop114407458.taobao.com/ **************************************************************************/ u8 delay_50,delay_flag,Flag_Stop; //延时相关变量 float ...

空空如也

空空如也

1 2 3 4 5
收藏数 100
精华内容 40
关键字:

平衡小车之家