imuncle.github.io icon indicating copy to clipboard operation
imuncle.github.io copied to clipboard

大叔的个人小站

Results 100 imuncle.github.io issues
Sort by recently updated
recently updated
newest added
trafficstars

今天突发奇想,**能不能访问并执行不在`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访问+解码**的方式访问感觉有点复杂,我通过开发者工具发现了下面的接口: ![image](https://user-images.githubusercontent.com/35989223/56423676-dc042900-62df-11e9-8acc-41013fe32c10.png) 在新窗口打开,发现这张图片的链接为...

web

好些时间没搞web了,今天突然有个不错的想法,实现的过程中遇到了`base64`编码,就研究了一番。 # Base64编码由来 为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就 不能通过邮件传送。这样用途就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。 最好的方法就是在不改变传统协议的情 况下,做一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示,问题就解决了。 Base64编码应运而生,Base64就是一种 基于64个可打印字符来表示二进制数据的表示方法。 # Base64索引表 ![image](https://user-images.githubusercontent.com/35989223/56418679-3befd500-62ca-11e9-9f74-0cdaf7202a9f.png) # 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...

web

还是要吐槽一下,STM32的IIC外设做的太复杂了(也没办法,为了避开Philip公司的专利)。之前用单片机的硬件IIC读取了MPU9250的数据(详见[MPU9250姿态解析](https://imuncle.github.io/content.html?id=39)),但是经常出现这样的情况:`原本数据读取正常,但是keil进入调试之后,点击运行,数据就全部读不到了`。 后来设置断点发现程序卡在了如下的位置: ![image](https://user-images.githubusercontent.com/35989223/56300343-5f9b0a00-6168-11e9-8d08-adee52bb8bdf.png) 我在这篇博客[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,一个用于读取,一个用于写入。 换了软件之后,之前的问题就没有遇到过了。开心 😃

RM

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...

RM

比赛用的电机基本都使用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,...

RM

# 问题描述 大疆给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,...

RM

昨天和今天兴致冲冲地重构了代码结构,结果今天下午烧程序一看,连灯都不闪,当场崩溃。进入调试后发现程序进入了下面的死循环中: ```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...

RM
tools

今年比赛的能量机关大变样,成了一个大风车,我们决定自己也做一个,官方的灯条是SK6812灯珠,每米144个灯珠,但是真的贵,最后选择了每米60珠的WS2811灯珠。 这一类的灯珠自带了IC,只需要一根信号线就能控制灯条上的每一个灯的颜色,且灯条长度理论上可以无限长。 这类灯条的控制方式都差不多,只是逻辑0和逻辑1的表现方式不同而已。我手里这款灯条的驱动方式如下: ![image](https://user-images.githubusercontent.com/35989223/55574813-e3ec9680-573f-11e9-8a63-a866e02634a4.png) ![image](https://user-images.githubusercontent.com/35989223/55574828-f1098580-573f-11e9-926c-6533ffd4755f.png) ![image](https://user-images.githubusercontent.com/35989223/55574854-041c5580-5740-11e9-9525-9fe6d2c5093f.png) ![image](https://user-images.githubusercontent.com/35989223/55574869-126a7180-5740-11e9-9889-bd87d5f144e7.png) 这里要吐槽一下,就是逻辑0和逻辑1的表示方法这里,我在百度上看到了三种版本,除了上面这种正确的外,另外两种如下: ![image](https://user-images.githubusercontent.com/35989223/55574973-58273a00-5740-11e9-8e1e-0c07e0517b10.png) ![image](https://user-images.githubusercontent.com/35989223/55575054-87d64200-5740-11e9-9636-799da355bdf8.png) 我真的是佛了,导致逻辑0和逻辑1的发送成了我驱动灯条最大的障碍。如果时序不对,那么整个灯条的颜色都是乱的。 后来我使出了绝招,用示波器看对应的灯条遥控器的输出信号,结果如下: * 逻辑0 ![image](https://user-images.githubusercontent.com/35989223/55575589-c3bdd700-5741-11e9-87fc-a35d4c3a67d5.png) * 逻辑1 ![image](https://user-images.githubusercontent.com/35989223/55575613-d801d400-5741-11e9-8333-7ba405c666c2.png) 从图中可以大致读出,`T0H`大约`300ns`,`T0L`大约`900ns`,`T1H`大约`600ns`,`T1L`大约`600ns`。 另外还要说一下,像这种一根信号线输出控制的一般逻辑电平都要求5V及以上,而STM32只能输出3.3V,所以这里使用arduino,但是arduino有一个致命的弱点,就是封装得太死,导致执行效率不高。 # arduino快速操作IO口 arduino操作IO口需要两步,第一步设置IO口为输出模式,第二步控制IO口电平高低,代码如下: ```c setup() { pinMode(8, OUTPUT); } void...

RM

DS18B20温度传感器提供9-Bits到12-Bits的高精度温度数据,我手上的封装类型如下: ![image](https://user-images.githubusercontent.com/35989223/55321737-f7cc9a00-54ac-11e9-8b88-a33eaef6bd5c.png) 一开始盲目自信,觉得这就是一个三极管,两端电压随温度改变,直接ADC读取电压值就行了,结果半天没反应,网上一搜数据手册才知道,这温度传感器中大有文章。 # DS18B20简介 ## 单线接口 DS18B20有其独特的单线接口方式,常见的通信协议都至少是两根线,而DS18B20利用它自己的一套通信协议实现了单线半双工通信。 通过这唯一的信号线可以实现数据读取,配置寄存器等操作,温度数据直接以数字信号形式返回来,比ADC高级多了。 ## 唯一ROM 每一个DS18B20在出厂的时候都自带了唯一的光刻64位ROM,前8位是传感器的ID,这样每一个DS18B20的ID都不一样,可以实现总线的功能,理论上总线上的设备可以无限多,而这些都是在一根信号线上完成的。 # 读取DS18B20数据 DS18B20对逻辑0和逻辑1有自己的定义,且收发不同,具体见下图: ![image](https://user-images.githubusercontent.com/35989223/55322318-99a0b680-54ae-11e9-8429-087a08d67939.png) DS18B20的可操作寄存器如下表所示: ![image](https://user-images.githubusercontent.com/35989223/55322423-e6848d00-54ae-11e9-8560-fdef3e1ba794.png) DS18B20的每一次读/写操作都必须按照下面的步骤进行: > ## TRANSACTION SEQUENCE > The transaction sequence for accessing the...

other

终于终于,我的这个外接小项目完成了。回过头来一看其实很简单,也就两部分,一部分是[步进电机](https://imuncle.github.io/content.html?id=27),一部分是[编码器](https://imuncle.github.io/content.html?id=32),今天就完成最后一步,把两者组合起来。 # 系统框图 我这个小demo的系统框图如下: ![default](https://user-images.githubusercontent.com/35989223/53868219-85dd6e00-4030-11e9-8089-6d45ea33f59f.png) # 程序逻辑 小项目的要求是实现步进电机与编码器同步旋转,所以需要实现步进电机的位置环控制,而因为我使用了步进电机驱动器TB6600,只需要发送一定数目的脉冲就可以了,反正我这也只是一个demo,所以也就没有弄什么角度传感器,直接开环控制。 ## 单脉冲模式 所以问题的关键就在如何产生指定数目的脉冲。我这里用到了TIM的单脉冲模式,也就是默认启动PWM后只产生一个脉冲,因为它默认一个周期后自动失能,这个时候需要再次使能PWM才能产生下一个脉冲。 我这里使用的是TIM5的CH1通道,周期1ms,占空比50%,单脉冲模式。 ## 根据编码器值控制步进电机角度 直接拿代码说话: 我定义了一个`RotateMotor()`函数: ```c void RotateMotor(void) { int32_t rotate = GetEncoderBias()>>15; if(rotate > round_count) { anticlockwise(); }...

other