big_uncle
big_uncle
前段时间搞裁判系统的时候接收过浮点数的数据,今天捣鼓超级电容的时候想到,可以把电容的电压反馈给主控板,这其中就会涉及到串口发送浮点数的问题。 由于浮点数本身存储方式与整形数据存储方式有很大不同,感兴趣的可以看这篇博客([32位单精度浮点数表示法](https://blog.csdn.net/liubing8609/article/details/78844121)),总之就是比较复杂,所以浮点数的发送我使用指针而不是按位发送。 定义的结构体如下: ```c struct { uint8_t header; float cap_v; uint8_t end; } tx_data; ``` 发送部分如下: ```c tx_data.header = 0xFF; tx_data.end = 0xFE; tx_data.cap_v = 3.1415f; HAL_UART_Transmit(&huart1, (uint8_t *)&tx_data, sizeof(tx_data),...
文章转自:[理解javascript中的立即执行函数(function(){})()](https://www.cnblogs.com/yanzp/p/6371292.html) 写的很好,在此转载记录一下。 --- 之前看了好多代码,都有用到这种函数的写法,但是都没认真的去想为什么会这样写,今天开始想学习下jquery的源码,发现jquery也是使用这种方式,用`(function(window, undefined){})(window)`包裹内部代码,于是进一步的去学习了下。 要理解立即执行函数`(function(){})()`,先了解些函数的基本概念(函数声明、函数表达式、匿名函数)。 ## 函数声明 使用function声明函数,并指定函数名。 ```javascript function setFn() { // coding } ``` ## 函数表达式 使用function声明函数,但未指定函数名,将匿名函数赋予一个变量。 ```javascript var setFn = function() { // coding } ```...
在GitHub上找到一个有趣的项目[FlappyLearning](https://github.com/xviniette/FlappyLearning),在线demo地址在[这里](https://xviniette.github.io/FlappyLearning/)。 前段时间正好我看了一些机器学习方面的教程,顿时起了兴趣,下载下来读了源码。 首先说,JavaScript语言是不适合搞机器学习和神经网络的,因为它对矩阵运算支持非常差,基本就靠for循环运算矩阵了。换言之,用js写出来的小程序肯定复杂不到哪去,正适合我这种初学者入门。 昨天我把这个项目的游戏部分剥离了出来,可以点这里[试玩](https://imuncle.github.io/example.html?repo=Funny&path=Flappy&doc=index.html),使用鼠标或手指点击控制小鸟煽动翅膀。 今天则研究了一下它的神经网络部分。 首先还是简单介绍一下什么是神经网络。 # 神经元 神经网络是模仿人脑的神经系统设计出来的,神经网络由很多神经元组成。典型的神经元的样子如下:  上面的神经元有三个输入(input)和一个输出(output),每一个输入有一个权重(weight),三者关系如下: `output = input1 * weight1 + input2 * weight2 + input3 * weight3` # 神经网络 神经网络就是由数个神经元组合起来的一个大整体:  其中,最左侧一列的神经元是输入层,最右侧一列是输出层,剩下中间的是隐藏层。可以看出,隐藏层越多,每一层神经元数目越多,整个系统就更复杂,就能实现更加复杂的功能。 #...
今天突发奇想,**能不能访问并执行不在`username.github.io`仓库下面的代码**,比如说我的GitHub账号另外有个仓库,里面有一个`index.html`,我想要做的是获取到这个文件的源代码并执行它,如果这样可以实现的话,那么我的博客文件就可以不局限与这个单独的仓库了,可以进行更加细致的分类。 查阅GitHub API发现,还真有这样的API接口,而且只要是public的repo,里面的文件都可以访问!如果访问private的话,就需要access token了。(文档原文见[Contents | GitHub Developer Guide](https://developer.github.com/v3/repos/contents/)) 但是这个API返回的是以base64的格式编码的内容,需要手动解码,base64编码详解及其解码方式见[base64编码详解-大叔的小站](https://imuncle.github.io/content.html?id=54)。 对于js文件,解码后文件的源码是以字符串的形式存储在浏览器中的,这里我使用`eval()`函数来执行js代码。这里我踩了一个坑。 开始我的js代码里面有如下语句: ```javascript window.onload = function(){ //... } ``` 这本是用于js代码开始执行的,但因为这里的特殊用途,页面早已加载完毕,所以这里的代码并不会执行,于是我修改为以下形式: ```javascript function start(){ //... } start(); ``` 这样就可以执行了。 对于图片文件,以**ajax访问+解码**的方式访问感觉有点复杂,我通过开发者工具发现了下面的接口:  在新窗口打开,发现这张图片的链接为...
好些时间没搞web了,今天突然有个不错的想法,实现的过程中遇到了`base64`编码,就研究了一番。 # Base64编码由来 为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就 不能通过邮件传送。这样用途就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。 最好的方法就是在不改变传统协议的情 况下,做一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示,问题就解决了。 Base64编码应运而生,Base64就是一种 基于64个可打印字符来表示二进制数据的表示方法。 # Base64索引表  # Base64编码原理 Base64的码表只有64个字符, 如果要表达64个字符的话,使用6的bit即可完全表示(2的6次方为64)。 因为Base64的编码只有6个bit即可表示,而正常的字符是使用8个bit表示, 8和6的最小公倍数是24,所以4个Base64字符可以表示3个标准的ascll字符。下面以`abc`的编码过程进行演示: 字符串 | a | b | c| - :--:|:--:|:--:|:--:|:--: ASCII | 97...
还是要吐槽一下,STM32的IIC外设做的太复杂了(也没办法,为了避开Philip公司的专利)。之前用单片机的硬件IIC读取了MPU9250的数据(详见[MPU9250姿态解析](https://imuncle.github.io/content.html?id=39)),但是经常出现这样的情况:`原本数据读取正常,但是keil进入调试之后,点击运行,数据就全部读不到了`。 后来设置断点发现程序卡在了如下的位置:  我在这篇博客[I2C总线死锁原因及解决方法](http://blog.sina.com.cn/s/blog_53e952220100s12m.html)中找到了原因所在: > 在I2C主设备进行读写操作的过程中.主设备在开始信号后控制SCL产生8个时钟脉冲,然后拉低SCL信号为低电平,在这个时候,从设备输出应答信号,将SDA信号拉为低电平。**如果这个时候主设备异常复位,SCL就会被释放为高电平。此时,如果从设备没有复位,就会继续I2C的应答,将SDA一直拉为低电平,直到SCL变为低电平,才会结束应答信号。** 而对于I2C主设备来说.复位后检测SCL和SDA信号,如果发现SDA信号为低电平,则会认为I2C总线被占用,会一直等待SCL和SDA信号变为高电 平。这样,I2C主设备等待从设备释放SDA信号,而同时I2C从设备又在等待主设备将SCL信号拉低以释放应答信号,两者相互等待,I2C总线进人一种 死锁状态。同样,当I2C进行读操作,I2C从设备应答后输出数据,如果在这个时刻I2C主设备异常复位而此时I2C从设备输出的数据位正好为0,也会导 致I2C总线进入死锁状态 而单片机的硬件IIC没办法手动解决这个问题,没办法,于是我改为使用软件模拟实现IIC,事实上,对于STM32来说,模拟实现IIC是最好的选择了。 软件模拟的实现代码我整理放在了[GitHub](https://github.com/imuncle/Embedded_Peripheral_Libs/tree/master/soft_i2c)上,封装了两个和HAL库差不多的API,一个用于读取,一个用于写入。 换了软件之后,之前的问题就没有遇到过了。开心 😃
2019版本的裁判系统延续了2018版本的裁判系统的部分功能,开放了用户向客户端发送自定义数据的通道,并且进一步地允许机器人间(非敌方)的信息交互。 通信协议本身很简单,发送也很简单,这里就不展示通信协议之类的了,倒是在调试过程中发现一个有趣的现象。 先贴出通信协议的帧头结构: 域|偏移位置|大小(字节)|详细描述 :--|:--:|:--:|:-- SOF|0|1|帧起始字节,固定值为0xA5 DataLength|1|2|数据段Data长度 Seq|3|1|包序号 CRC8|4|1|帧头CRC8校验 问题就出现在`DataLength`这里,我的发送的数据段长度是19,十六进制表示也就是0x0013。 关键代码如下: ``` uint8_t* protocol_packet_pack(uint16_t cmd_id, uint8_t *p_data, uint16_t len, uint8_t sof, uint8_t *tx_buf) { uint16_t frame_length = HEADER_LEN + CMD_LEN...
比赛用的电机基本都使用CAN通信,最简单的步兵机器人上有四个底盘电机,两个云台电机和一个拨弹电机,一共7个电机,如果这些电机都挂载在同一个CAN总线上,容易造成总线阻塞,具体表现为车子跑着跑着就疯掉或不动(电机会自动保存最近一次接受到的数据2s,然后关闭输出),并且无论遥控器怎么操作,电机都不会动。 进入调试发现代码卡在如下位置: ```c if(HAL_CAN_AddTxMessage(hcanx, &TxMessage, TxData, (uint32_t *)CAN_TX_MAILBOX0)) { Error_Handler(); } ``` 可以看到如果发送失败就会进入一个死循环。其实这个循环原本是用来执行闪灯程序提醒用户错误类型的,结果因为这个死循环搞死了整个程序。理论上去掉这个死循环CAN发送是可以正常进行的,只是可能有几次因为邮箱满了发不出去。 其实CAN的发送有三个邮箱,之前我一直只用了其中一个邮箱,完全可以用上其他几个邮箱,这样一个CAN总线上可以挂载的外设数就成倍增加了。 具体操作如下: ```c /** * @brief ID为1~4的电机信号发送函数 * @param ID为1~4的各个电机的电流数值 * @retval None */ void CanTransmit_1234(CAN_HandleTypeDef *hcanx,...
# 问题描述 大疆给RoboMaster比赛专门做了一版遥控器固件,通信协议很简单,每14ms发送一帧18字节长度的数据,其中没有任何用于检验数据是否接收正确的信息。 虽然14ms的间隔很长,如果不是运气太差,是不会恰好在数据接收途中发生中断什么的,但可能性还是有的,更何况实际比赛的时候遥控器非常多,比赛赛场有,备场有,可能接收到杂其他遥控器发过来杂乱数据,如果程序没有一定的容错性,遥控器数据可能会出错,车子会莫名其妙地疯跑。 另外在日常调试的时候,往往keil中进入调试,点击运行后,发现遥控器数据错乱,此时要软件复位才行,这也是因为上述原因。在进入调试的时候,遥控器接收机正常通电工作,反馈数据,在点击运行的时候恰好是接收机在反馈一帧数据的时候,导致单片机直接从数据帧的中途接收,使得之后的数据全部错位,数据全乱了。 这种问题还出现在,先开遥控器后给机器人上电的时候,有时候反应为机器人无法遥控,有时候反应为开机就疯,这都是遥控器的数据出错。 # 解决办法 之前研究裁判系统接收数据(可以点[这里](https://imuncle.github.io/content.html?id=8)回顾)的时候,当时遇到了串口空闲中断,这次又派上用场了。 ## 串口空闲中断 开启空闲中断后,单片机会在接收完一帧数据后触发接收中断,与设置的接收字节无关(当然设置的接收字节数要大于一帧可能的最大字节数)。空闲中断非常适合于接收不定长数据,既然是不定长,当然就无法实现接收的字节数,一个一个字节接收又显得太麻烦,空闲中断就完美地解决了这个问题。 遥控器的数据是定长的,我们可以用空闲中断来构造一个校验位,只有接收到的一帧数据是18位的才认为遥控器的数据正确, # 实现代码 STM32CubeMX里面的串口配置很简单,波特率100Kbps,8位,停止位1位,偶校验,无硬件流,开启DMA和global interrupt,点击生成代码,完事。 打开新建的工程,首先打开串口空闲中断: ```c MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); HAL_UART_Receive_DMA(&huart1, rc_data,...
昨天和今天兴致冲冲地重构了代码结构,结果今天下午烧程序一看,连灯都不闪,当场崩溃。进入调试后发现程序进入了下面的死循环中: ```c /** * @brief This function handles Hard fault interrupt. */ void HardFault_Handler(void) { /* USER CODE BEGIN HardFault_IRQn 0 */ /* USER CODE END HardFault_IRQn 0 */ while...