stm32官方出了个免费的IDE,可以很方便地配置(初始化)stm32项目。
文章目录
安装相关
安装
下载地址【STM32的集成开发环境 】
(不要安装1.13.0及以上版本。新版的用不了山寨的st-link来调试程序,会卡在什么需要升级固件这里)
参考:
https://blog.csdn.net/qq_38113006/article/details/108699060
https://blog.csdn.net/qq_42038029/article/details/99735688
不要放在中文目录下执行安装程序,下面这个错误报错就是放在中文目录下运行的结果:
NSIS ERROR: error launching installer
st-link utility 安装
下载:https://www.st.com/zh/development-tools/stsw-link004.html
使用:https://blog.csdn.net/li520_fei/article/details/120082735
工程、芯片配置流程
https://www.cnblogs.com/pathfinder-world/p/13943911.html
黑色主题
软件自带个黑色主题,感觉够用了。
菜单Window–>Preference–>General–>Apearance–>Theme
占用C盘的问题
软件有个路径默认是存放在C盘的,他下载占用的空间还是挺大的,最好自己手动更改一下。
菜单Window–>Preference–>STM32Cube–>Firmware Updater–>Repository Setup
中文字体小的解决
在默认的配置中,假如在代码文件中写了中文,在显示时只会占一个英文字符位置,非常难看,如下图所示:
假如想正常显示,可以参考这里更改一个地方:【STM32开发笔记109:在STM32CubeIDE中调整字体显示】
window–>preference–>General–>Apearance–>Colors and Fonts–>Basic–>Text font, 在脚本处选择中欧字符,如下图所示:
这下子显示正常了。
中文注释乱码
【STM32CubeMX/IDE生成Code中的中文字符乱码】
【STM32CubeIDE常见问题】
假如我们在main.c或者其他它自动生成的文件中写了中文的注释,每次我们在.ioc界面中修改参数然后返回源码编辑时,原来的中文注释都会大概率部分乱码,如下图所示:
解决办法是添加环境变量 JAVA_TOOL_OPTIONS,设置其值为:-Dfile.encoding=UTF-8
注意这样设置之后,只保证后面新输入的中文不乱码,之前乱码的救不回来的。
工程导入
有时候我们在一台电脑A上面建立好工程后,需要把工程代码拷贝到电脑B继续用。那么工程文件夹拷贝到电脑B后,该如何使用?
这里有两种办法。
import Existing Projects into Workspace
推荐使用这种方式
从菜单 File–>Import
可以勾选Copy projects into workspace,这样代码就会被拷贝进工作空间,方便管理。
Import an Existing STM32CubeMX Configuration File(.ioc)
不推荐使用这个。因为这种方式是只会导入它STM32CubeMx相关的代码部分。也就是说,自己写的东西、添加的头文件啥的都不会导入进来。一般来说不是我们想要的结果。
从菜单 File–>Import an Existing STM32CubeMX Configuration File(.ioc)
flash相关
读写保护
在stm32cubeide中,貌似没有直接提供 Flash_EnableReadProtection 这个函数,而是需要自己实现。
参考:[STM32G031 程序设置读保护]、【如何禁止STM32通过SWD和ISP升级保护自己的产品 HAL_FLASH_Program】
注意:
1.一定要执行 HAL_FLASH_OB_Launch()这个函数,而且在第一次执行之后,还需要芯片整个断电、重新上电才能生效。假如只是单纯的按复位的话,芯片会一动不动,像坏了一样,记得,要重新上电。
2.千万、千万、千万不要轻易选择level2,选择了之后,是永久性的,就回不去了,外部器件完全无法读写芯片了。我就是这样,把一块芯片搞费了。唉。 【STM32烧熔丝】
void Flash_EnableReadProtection(void)
{
FLASH_OBProgramInitTypeDef OBInit;
__HAL_FLASH_PREFETCH_BUFFER_DISABLE();
HAL_StatusTypeDef ret =0;
HAL_FLASHEx_OBGetConfig(&OBInit);
printf("RDPLevel:0x%02x\r\n", OBInit.RDPLevel);
if(OBInit.RDPLevel == OB_RDP_LEVEL_0)
{
OBInit.OptionType = OPTIONBYTE_RDP;
OBInit.RDPLevel = OB_RDP_LEVEL_1; // 千万不能用level2, level2是写了一次就不能回来了
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
ret = HAL_FLASHEx_OBProgram(&OBInit);
printf("HAL_FLASHEx_OBProgram:%d\r\n", ret);
ret = HAL_FLASH_OB_Launch(); // 要加这个,否则重启、断电重启后都不生效
printf(" HAL_FLASH_OB_Launch:%d\r\n", ret);
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
}
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
}
主要用到了函数HAL_FLASHEx_OBProgram ,其中的保护等级:level0-不保护
参考【stm32专题二十九:Flash 读写保护】
在启动了读保护之后,假如在烧写时想解除的话,参考这里:【STM32读保护】
flash的读写(断电保存数据)
https://blog.csdn.net/yannanxiu/article/details/52837411
FSMC读写外部flash
【AN2784: Using the high-density STM32F10xxx FSMC peripheral to drive external memories】
SDIO的使用
【STM32CubeMX系列09——SDIO(SD卡读写、SD卡移植FATFS文件系统)】
【stm32 CubeMx 实现SD卡/sd nand FATFS读写测试】
【STM32F1 HAL库读写SD卡的操作要点】
记得要修改函数MX_SDIO_SD_Init中的hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
1、sd/tf卡的最高支持25MHz的时钟。
2、其读写的最小单位是block,1block一般是512byte。
3、在写之前不用擦除。但是不擦除的话要注意原来数据的干扰。(也有说要擦除的【stm32 SDIO tf卡 速度(stm32 sd卡读写速度)】,暂时还没确定,到时候自己测试一下)(经过测试,好像写之前不用擦除)
当尝试通过stm32的usb_device把整个系统弄成sd卡读卡器时,sdio部分,请查看下面这个文章。此时,sdio的分频系数搞成0也是可以的。
[ [已解决] STM32F407,USB(3300,MSC),SDIO,DMA,读写卡速度【已解决】 ]
假如你不外加芯片,USB_OTG_FS模式下,最快也只能达到12MBits/s,也就是1.5MB/s左右。上面文章中的是额外加了usb芯片,启用了USB_OTG_HS才有的8MB/s的速度。
int8_t ret = USBD_FAIL;
if( HAL_SD_ReadBlocks_DMA(&hsd, buf, blk_addr, blk_len) == HAL_OK )
{
ret = USBD_OK;
}
if( USBD_OK == ret )
{
while(HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY){};
while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){};
}
------
int8_t ret = USBD_FAIL;
if( HAL_SD_WriteBlocks_DMA(&hsd, buf, blk_addr, blk_len) == HAL_OK )
{
ret = USBD_OK;
}
if( USBD_OK == ret )
{
while(HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY){};
while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){};
}
return ret;
虚拟串口
配置:https://blog.csdn.net/weixin_64946562/article/details/128886320
使用:https://blog.csdn.net/weixin_51002159/article/details/126644375
这里简单记录一下用法:发送数据用 CDC_Transmit_FS 即可;假如想收数据,可以通过在 USB_DEVICE/APP/usbd_cdc_if.c文件中的 CDC_Receive_FS 函数中加入自己的小手段(全局变量、指针、全局函数等),来实现同步、异步的处理。CDC_Receive_FS 这个函数应该是系统在接受到数据后中断调用的,我们添加上去的函数也要注意一下,避免进行太耗时的操作。
虚拟U盘
这个帖子【 [ARM] 一直想把STM32的内部FLASH做成个小U盘,参考了不少,一直不成功 】里面有个回帖的人bg4uvr,他给出了一个解决办法【bg4uvr / stm32_usb_mass_hal 】。
需要注意的是,这里的这个MSC_MEDIA_PACKET也设置成和usbd_storage_if.c中的USER_BLK_SIZ一致。
帖子上使用的是STM32F103C8T6,他的flash是按页排放的。假如使用STM32F407的,它的flash是按照sector来存放的,需要参考这个文章来进行函数修改:【基于hal库实现stm32内部flash的读取】
假如你只是想尝试一下,那可以学我一样直接把存储区放到内存去(由于存放在内存区,每次重新上电后都需要格式化):
//#define FLASH_START_ADDR 0x08010000 //U盘占用FLASH空间起始地址
#define USER_BLK_NBR 64 //U盘块数量
#define USER_BLK_SIZ 1024 //U盘块大小
uint8_t myBuffer[USER_BLK_NBR * USER_BLK_SIZ] = {0}; // 我的u盘
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
/* USER CODE BEGIN 3 */
UNUSED(lun);
*block_num = USER_BLK_NBR;
*block_size = USER_BLK_SIZ;
return (USBD_OK);
/* USER CODE END 3 */
}
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
UNUSED(lun);
UNUSED(buf);
UNUSED(blk_addr);
UNUSED(blk_len);
if(lun == 0)
{
memcpy(buf,(uint8_t *)(myBuffer + blk_addr * USER_BLK_SIZ), blk_len * USER_BLK_SIZ);
return USBD_OK;
}
return (USBD_OK);
/* USER CODE END 6 */
}
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 7 */
UNUSED(lun);
UNUSED(buf);
UNUSED(blk_addr);
UNUSED(blk_len);
if(lun == 0)
{
memcpy(myBuffer + blk_addr * USER_BLK_SIZ, buf, blk_len * USER_BLK_SIZ);
return USBD_OK;
}
return (USBD_OK);
/* USER CODE END 7 */
}
文件系统
我用的spi芯片为W25Q16,容量为16Mbit=2MByte.
以这个为主,基本可以直接使用: 【STM32CubeMX学习笔记(25)——FatFs文件系统使用(操作SPI Flash)】
从这里修正一下文件系统创建,但是不要用他的 GET_BLOCK_SIZE: 【为SPI FLASH移植fatFs文件系统心得 (二)】
BYTE workBuffer[4096 * 2]; // 最好是块的两倍以上
int len = sizeof(workBuffer);
retUSER = f_mkfs(USERPath, FM_FAT | FM_SFD, 4096, workBuffer, len); // 第三个参数填0也可以,但是填4096会稳妥一点
// workBuffer、第三个参数的大小设置不合理的话,有可能会导致文件一部分被擦除。
这里可以知道id 【【STM32CubeMX学习】SPI读写W25Q16】
芯片唯一码
这个文章【STM32HAL库-读取芯片维一码(UID)】里面提供了很多种方法,我觉得最方便的还是用自带的hal函数最方便
uint32_t uuid[3];
uuid[0] = HAL_GetUIDw0();
uuid[1] = HAL_GetUIDw1();
uuid[2] = HAL_GetUIDw2();
加密库
假如我们需要在stm32中使用一些常用的加密算法对数据进行验证、加密的话,可以直接使用官方的加密库
【STM32Cube的STM32加密固件库软件扩展(UM1924) 】
这个库提供了各种加密算法的函数:AES-128, AES-192, AES-256 bits、HASH: 、RSA with PKCS#1v1.5:ECC (elliptic curve cryptography)等等。
使用方法:
参考官方的使用流程
【Cryptographic library 】、【Getting started with the Cryptographic Library 】
开启芯片的crc
下载并解压,取其三个文件夹
将下载下来的文件解压,然后把MiddleWare文件夹里面的三部分的东西拷贝到某个文件夹下:
在项目工程中加入其include路径、lib路径、lib文件、源文件路径
然后添加include路径、lib路径、lib文件
include路径
lib路径
lib文件
其中lib文件要根据你的stm32芯片进行选择:【微控制器和微处理器】
源文件路径
修改interface文件夹里面的cmox_low_level_template.c文件
然后把interface文件夹里面的cmox_low_level_template.c 文件重命名为cmox_low_level.c
并在文件内容中,增加你当前所使用的芯片的相关头文件
但是这样设置完之后,编译还是会有问题:
undefined reference to `__HAL_RCC_CRC_RELEASE_RESET’
原因是在cmox_low_level.c文件中用到了这个函数(宏定义),但是实际上我们的这个型号(stm32f103ze)的hal库中没有这个函数。直接注释掉就行。
注:在我的另外一块芯片stm32f407zg中,有这个函数。总之有的话就留着,没的话就注释。
开始使用
ok,接下来就可以参考例程来使用这个库了。例程放在Projects这个文件夹里面。
在线升级 IAP
【干货 | 详解 stm32 在线 IAP 升级】
【stm32在线升级方案】
可以利用上面提到的flash读保护、加密库,对app.bin进行加密、解密,实现对程序的隐藏,防止别人拷贝、破解我们的stm32程序。
这个暂时用不到,先挖个坑。
AB相编码器的使用
https://blog.csdn.net/LeonSUST/article/details/89520902
https://blog.csdn.net/wang328452854/article/details/50579832
但是这种模式的话,不方便实现每个脉冲都触发一次中断(其实也可以通过一个额外的中断输入引脚来实现)。看具体需求了。
串口相关
串口
https://blog.csdn.net/qq_42038029/article/details/103835984
需要注意串口的中断接收函数的用法
// 这个函数是指开启中断接收指定字节的数据,接收够之后,就会调用用户的中断回调函数《void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)》
//然后中断会在调用了用户回调函数之后失效。
//因此,假如需要持续接收数据,还要在回调函数那里重新调用这个函数,开启中断接收
HAL_UART_Receive_IT(&huart1, aRxBuffer, 1);
串口实现自定义通讯协议:
https://blog.csdn.net/u010779035/article/details/103764852#comments
通过串口printf
https://blog.csdn.net/qq_36075612/article/details/96851644
在使用时,有时候必须要加上回车符才行
// 这样不会触发发送
printf("=-----wtf-----=");
// 这样才行
printf("=-----wtf-----=\r\n");
定时器的使用
https://blog.csdn.net/u010779035/article/details/104134877
定时器的PWM捕获
https://blog.csdn.net/as480133937/article/details/99407485
can总线相关
can的使用
https://blog.csdn.net/qq_42635852/article/details/109287203
https://www.jianshu.com/p/0f6a68d1a8d7
can的工作模式:loopback、silent、loopback combined with silent
https://blog.csdn.net/flydream0/article/details/8170368
can过滤器详解
https://blog.csdn.net/flydream0/article/details/52317532
https://blog.csdn.net/qq_35480173/article/details/98878309
//初始化can的过滤器
void can_filter_init(void)
{
CAN_FilterTypeDef filter;
uint32_t stdId = 0x123; //这里写入两个CAN id, 一个为标准canID
uint32_t extId = 0x1800f001; //一个扩展canID
filter.FilterBank = 0;
filter.FilterMode = CAN_FILTERMODE_IDLIST;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterIdHigh = stdId << 5; //将基本id放到STID中
filter.FilterIdLow = 0 | CAN_ID_STD; //设置IDE位为0
//左移3是因为canid最多只能有11+18=29位,所以需要去掉高32-29=3位
//然后此时左侧的16位是对齐寄存器的STID+EXID[17:13]的,所以直接右移,取下来就行。
filter.FilterMaskIdHigh = ((extId << 3) >> 16) & 0xffff;
//取最右面16bit数值, 加上对IDE位设置1,将其对赋值给寄存器
filter.FilterMaskIdLow = (extId << 3) & 0xffff | CAN_ID_EXT;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter.FilterActivation = ENABLE;
if(HAL_CAN_ConfigFilter(&hcan, &filter) != HAL_OK)
{
Error_Handler();
}
}
常用操作
代码补全
alt + /
生成hex
项目右键–》properties。。。
添加第三方源码
参考下图:右键项目properties–C/C++ General–Paths and Symbols–Includes以及SourceLocation。
记得 Includes 以及 SourceLocation,这两个都要设置,一个对应头文件,一个对应源文件。
IIC、I2C
I2C使用
https://blog.csdn.net/u010779035/article/details/104346201