访问 Arduino 官方网站软件下载页面:https://www.arduino.cc/en/software 提供了多种版本的 IDE 可供选择,最上边的两个一个是在线 IDE,另一个是供下载到本地运行的 IDE。 前者在编辑器方面提供了与桌面版几乎一样的体验,代码会保存在云上,而且不需要安装板载包,还包含了大部分流行的库,如果需要也可以上传自己的库。不过我认为下载 IDE 并在本地开发更适合初学者。 下载软件是免费的,你也可以在下载页面选择捐赠以支持 Arduino 开发团队的工作。下载安装后初次运行会安装一些驱动程序和下载一些包,默认允许即可。 此外还可以使用 Visual Studio Code + PlatformIO 等,后续单独介...
分类 嵌入式开发 下的文章
IAR for RL78 使用未声明函数引起的问题
在使用 IAR for RL78 1.30.3 时遇到一个奇怪的问题: 调用一个函数(反转 IO 口电平)时总不能达到预期结果,一通分析也查不出个所以然,最后只能通过反汇编结果分析了一下。向函数传递的是 uint8_t 参数(函数定义 void gpio_toggle(uint8_t port)),使用的却是 16 位的 AX 寄存器,低 8 位存放在 X 寄存器,但进入函数处理时却是从 A 寄存器中取值的(而不是 X),这就导致参数传递错误了。 还发现,奇怪的是在别的地方调用同样的函数却是正常的!传递参数用的是 8 位的寄存器 A: 搞糊涂了,为什么同样的函数在不同文件中调用的结果是不同的呢?而且后续还发现出问题的那个函数所在文件的其他函数也会有类似的问题。于是疯狂地怀疑这儿怀疑那儿,还怀疑是不是这个低版本的编译器存在 Bug 😅 冷静下来后排查了一下编译时的 Warning: Warning[w6]: Type conflict for external/entry "gpio_toggle", in module ... against external/entry in module hal_gpio; prototyped function vs K&R function 其实警告信息说的不够直白,实际的情况是调用了没有声明的函数(没有引用头文件或头文件中没有声明函数),所以加上声明 extern void gpio_toggle(uint8_t port); 就正常了。 反思:我没把这条警告信息当回事儿,想当然地认为编译链接不报错就应该是正确地链接了,如果是链接到找不到的函数会报错的。实在是大意了😂 找不到函数你倒是不要给我瞎传参数啊,报错也好啊!不知道更新版本的 IAR 是否存在同样的问题,年轻人真是不讲武德啊 😎 总结:认真对待每一条警告信息,耗子尾汁儿! 后记:还真是令我印象更深刻了,后续又遇到了这个问题。把一个 enum 参数传递给了函数,但进到函数后却不能正确地得到这个参数,忽然想到以前遇到过类似的问题,一查还真是没有引用相关头文...
MDK-ARM(Keil)使用 GCC 编译器
MDK 自带的编译器是 ARMCC 或 ARMCLANG(Arm Compiler 6),有时需要使用 GCC 编译器(例如需要使用 GCC 编译的静态库)。当然,用 GCC 不一定非要用 MDK IDE。 先去 ARM 官网下载 GNU 工具链(Arm GNU Toolchain) Arm GNU Toolchain is a community supported pre-built GNU compiler toolchain for Arm based CPUs. Arm GNU Toolchain releases consists of cross toolchains for the following host operating systems: GNU/Linux Available for x86_64 and AArch64 host architectures Available for bare-metal and Linux targets Windows Available for x86 host architecture only (compatible with x86_64) Available for bare-metal and Linux targets macOS Available for x86_64 host architecture only Available for bare-metal targets onl...
新唐单片机调试工具 NuConsole
常用调试信息交互方法是使用串口。除此之外还有 Semihosting(半主机)方式,但需要调试接口外连接其他接口(例如 SWD 调试时接 SWO 接口)。 新唐的 NuConsole 则是通过在 RAM 中开辟一块信息块的方式实现通过调试接口(不需要 SWO 等额外接口)交互。也算是一种比较好的调试思路和手段了,不过依然需要试用调试器才能用,虽然不影响单片机的实时性,但也不如串口打印这么简单和通用,只适合开发时使用。 使用时需要将: NuConsole.h/.c、NuConsole_Config.h、NuConsole_Retarget.c 加入工程。此外还要做一些配置工作,详见说明文档 NuConsole_HowTo.pdf(在 C:\Program Files (x86)\Nuvoton Tools\NuConsole 可以找到说明文档和例程)...
ARM 开发板使用 NFS 挂载 Ubuntu 文件夹
采用 NFS 实现远程挂载,ARM 开发板可以直接运行主机上的程序,而不需要下载到开发板上。 1、查看开发板 Linux 内核是否支持 NFS 执行 cat /proc/filesystems, 若有一行为 nodev nfs 则开发板 Linux 内核支持 NFS,反之需要配置内核;也可以使用动态模块的形式编译。 2、主机安装 nfs 服务 sudo apt-get install nfs-kernel-server 配置 nfs 输出目录: vim /etc/exports 在该文件中添加一行: /home/user *(rw,sync,no_root_squash) # 根据实际情况修改 /home/user 为想要的路径就行了 * 表示任意 IP 地址的主机,如果是 192.168.1.100,就表示只允许 192.168.1.100 挂载。 rw: 可擦写的权限 ,ro: 只读的权限。 sync: 资料同步写入到内存与硬盘当中 ;async: 资料会先暂存于内存当中,而非直接写入硬盘。 no_root_squash:登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个系统账号的身份。 主机启动NFS服务: sudo service nfs start # 或者 sudo service nfs-kernel-server restart 3、在开发板上挂载主机上的文件夹 mkdir /mnt/nfs mount -t nfs -o nolock 172.16.9.93:/home/matt /mnt/nfs 172.16.9.93 是主机 IP,/home/user/test 是主机共享目录,/mnt 表示将该共享目录挂载到 ARM 开发板 /mnt 目录下。 错误: mount: wrong fs type, bad option, bad superblock on... 解决方法: sudo apt-get install nfs-commo...
Cortex-M3 HardFault_Handler 调试心得
这几天在 MDK 下调试 STM32 在进入 HardFault_Handler 异常中端原因的问题上花费了不少周折 简单来说应该采取下面的查找思路: 1、进入 HardFault_Handler 后根据 LR 确定异常发生处所使用的栈指针是 MSP 还是 PSP 2、查看 PSP(假设是 PSP)的值,在 RAM 中找到 PSP 所指的区域 3、地址按从低到高的顺序分别存放了 R0,R1,R2,R3,R12,LR,PC,XPSR 的顺序找出 PC 的值 4、根据 PC 的值找到异常程序的位置 5、分析异常程序位置的 FLASH 及 RAM 访问相关方面是否有异常值,一般是 RAM 被异常修改导致从此处取到了错误的访问地址引起的。 在 HardFault_Handler 打断点,在 Call Stack + Locals 窗口查看调用顺序,在某个入口点右键执行 Show Caller Code 跳到响应程序的位置再仔细查看出错原因...
STM32F1 库函数的宏定义
1、USE_STDPERIPH_DRIVER 宏定义 要在编译器中预定义这个宏: 而且在编译器中仅仅定义这一个宏就可以了。STM32F10X_HD 这类的宏在编译器中选好 MCU 型号后就自动确定下来对应的宏了。 2、HSE_VALUE 宏定义 HSE_VALUE 需要根据实际使用的晶体频率修改,RCC_GetClocksFreq 中会用它来算出系统时钟。因为 USART_Init、I2S_Init、I2C_Init 用到了 RCC_GetClocksFreq,所以如果不修改 HSE_VALUE 为实际情况下的值会出现串口波特率错误等问题。不要直接修改 stm32f10x.h 中的定义,而要在 stm32f10x_conf.h 中重新定义 HSE_VALUE: #undef HSE_VALUE #define HSE_VALUE ((uint32_t)xxx...
STM32 CAN 总线通信
CAN 是控制器局域网络 (Controller Area Network, CAN) 的简称,是由以研发和生产汽车电子产品著称的德国 BOSCH(没错,就是那个卖电动工具的博世)公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。 在北美和西欧,CAN 总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以 CAN 为底层协议专为大型货车和重工机械车辆设计的 J1939 协议。 CAN 采用多主工作方式,节点之间不分主从,但节点之间有优先级之分,通信方式灵活,可实现点对点、一点对多点及广播方式传输数据,无需调度。CAN 采用的是非破坏性总线仲裁技术,按优先级发送,可以大大节省总线冲突仲裁时间,在重负荷下表现出良好的性能。CAN 采用短帧结构传输,每帧有效字节为 8 个,传输时间短,受干扰的概率低。而且每帧信息都有 CRC 校验和其它检错措施,保证数据出错率极低。当节点严重错误时,具有自动关闭功能,使总线上其它节点不受影响,所以 CAN 是所有总线中最为可靠的。CAN 总线可采用双绞线、同轴电缆或光纤作为传输介质。它的直接通信距离最远可达 10km,通信速率最高达 1M bps(通信距离为 40m 时),总线上可挂设备数主要取决于总线驱动电路,最多可达 110 个。但 CAN 不能用于防爆区。 CAN网拓扑结构 CAN 是半双工的。收发数据要分时进行。不管 CAN 网络上挂多少设备,在同一时刻只能有 1 个发送数据。如果有多个需要同时发送则只有优先级别高的先发送,其它等待。控制器有收发两个接口,但是收发器的两个接口变成了 High 和 Low。CAN 总线式两线制的。在这点上可以与 Uart 控制器与 RS-485 收发器来类比。 CAN 协议目前有 2.0A 和 2.0B。CAN 2.0A 是 CAN 协议的 PART A 部分,此部分定义了 11bit 的标识区 。 CAN 2.0B 是 CAN 协议的扩展部分,也叫 PART B,定义了 29bit 的标识区,设计上与 CAN2.0A 兼容。通常支持 2.0B 协议的芯片同时也支持 2.0A。 STM32 单片机的 CAN 控制器接口有收、发两个口,互联型有两个 CAN 控制器。两个 CAN 都分别拥有自己的发送邮箱和接收 FIFO,但是他们共用 28 个滤波器。通过 CAN_FMR 寄存器的设置,可以设置滤波器的分配方式。 STM32 的过滤器组最多有 28 个(互联型),但是 STM32F103ZET6 只有 14个(增强型),每个过滤器组由 2 个 32 为寄存器,CAN_FxR1 和 CAN_FxR2 组成。过滤器有两种工作模式:屏蔽位模式和标识符列表模式。工作在屏蔽位模式时 CAN_FxR2 为 1 的位指示了接收到的标识位必须与 CAN_FxR1 相对应的位的 0 或 1 完全一致才能通过过滤器。如果 CAN_FxR2 全为 0 也就意味着不进行任何过滤。工作在标识符列表模式时 CAN_FxR1 和 CAN_FxR2 互不相干,CAN_FxR2 也是一个像 CAN_FxR1 的寄存器,当接收到的标识符与 CAN_FxR1 或 CAN_FxR2 完全匹配时才能通过过滤器。 STM32 提供了两种测试模式,环回模式和静默模式。环回测试模式不需要短接外部的两个脚,它是在内部环回的...
STM32 定时器
预分频器(TIMx_PSC) 预分频器的值 PSC[15:0],计数器的时钟频率 CK_CNT 等于 fCK_PSC / (PSC[15:0] + 1)。也就是说 PSC[15:0] 等于分频数 - 1。 自动重装载寄存器(TIMx_ARR) 自动重装载的值 ARR[15:0],用于作为计数器的初始值(向下计数)或溢出值(向上计数)。下图的 ARR 被设置为 0x36,溢出发生在计数值为 0x36 的后部,所以实际计数了 0x37 个数(从 0 开始)。所以 ARR 的值应该为 计数值 - 1。 PWM PWM 模式 1 在向上计数时,一旦 TIMx_CNT < TIMx_CCR1 时通道 1 为有效电平,否则为 无效电平;在向下计数时,一旦 TIMx_CNT > TIMx_CCR1 时通道 1 为无效电平(OC1REF=0),否 则为有效电平(OC1REF=1)。 PWM 模式 2 在向上计数时,一旦 TIMx_CNT < TIMx_CCR1 时通道 1 为无效电平,否则为 有效电平;在向下计数时,一旦 TIMx_CNT > TIMx_CCR1 时通道 1 为有效电平,否则为无效电 平。 CC1P:输入/捕获 1 输出极性(Capture/Compare 1 output polarity) CC1 通道配置为输出: 0:OC1 高电平有效 TIM_OCPolarity_High 1:OC1 低电平有效 TIM_OCPolarity_Low 例如: TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PMW模式1(向上计数时CNT < CCR2时输出低电平) TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = TIM_TimeBaseStructure.TIM_Period / 2; //50%占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //低电平为有效电平 TIM_OC2Init(TIM2, &TIM_OCInitStructure)...