0 输入信息
- 资料手册:配套资料
- 100ASK_STM32MP157 M4 用户手册
- 嵌入式 Linux 应用开发完全手册
- 核心板芯片数据手册
- 开发板原理图
- 问答社区:前往
- 视频教程:
- STM32MP157:Bilibili、百问网
- Linux:2021韩顺平 一周学会Linux
1 开发环境
开发工具
- IDE: STM32CubeIDE
- 串口调试:MobaXterm
- 代码编辑:VSCode
- 交叉编译:gcc-arm-none-eabi
- 代码调试:openocd、ST-Link
开发流程
- 软件安装
- 创建模板
- 创建工作区
- 新建STM32 Project
- 芯片选择区输入STM32MP157D,选择TFBGA361
- 创建项目名称
- 结合原理图进行引脚设置
- 菜单栏Project选择Generate Code生成项目程序框架模板
- 使用HAL库调用函数接口进行应用开发
- 编译上传
- 选择项目下CM4文件夹右键Build Project完成编译
- 编译完成后会自动生成Debug目录,其中以.elf为后缀的文件即为二进制目标文件
- 通过J-Link或者ST-Link把.elf文件烧写进系统
- PC连板卡USB串口、网口
- 板卡拨码开关设置为010,eMMC启动
- 串口查询板卡网口地址
- 修改PC连接的网口地址与板卡处于同一网段,最后3位IPv4地址不同
- MobaXterm上通过SSH方式连接板卡进入linux系统
- 将.elf上传到lib/firmware
- 加载固件,启动M4运行固件
M4调试
- 工程模式
启动方式 001
类似 STM32 单片机烧写到 RAM,M4 启动,无需 A7 加载。因为烧写到 RAM 里,掉电程序 消失,一般用于调试。
调试失败,尝试断开串口、网口。
- 量产模式
启动方式 010
A7 启动后,通过 Remoteproc 服务启动 M4,固件先被存放在 Flash,掉电不会消失,但 需要 A7 先启动,引导 M4 启动,用于实际部署。
项目结构
- CA7: 包含 A7 处理器的各个阶段(TF-A、U-Boot、Kernel)的可能用到的设备树文件。
- CM4:M4 工程的核心,里面有一个 M4 的工程项目。
- Common:CMSIS 设备外设访问层,会被包含到 M4 工程里面。
- Driver:CMSIS 库、HAL 库,会被包含到 M4 工程里面。
- .mxproject 和 ProjectDemo.ioc:STM32CubeMX 工程入口和配置信息。
- .project:本 STM32CubeIDE 工程入口。
├─CA7
│ ├─.settings
│ └─DeviceTree
│ └─mp157
│ ├─kernel
│ ├─optee-os
│ ├─tf-a
│ └─u-boot
├─CM4
│ ├─.settings
│ ├─Core
│ │ ├─Inc
│ │ ├─Src
│ │ └─Startup
│ ├─Debug
│ │ ├─Common
│ │ │ └─System
│ │ ├─Core
│ │ │ ├─Src
│ │ │ └─Startup
│ │ ├─DemoDriver
│ │ └─Drivers
│ │ └─STM32MP1xx_HAL_Driver
│ ├─DemoDriver
│ ├─Drivers
│ │ └─STM32MP1xx_HAL_Driver
│ └─RemoteProc
├─Common
│ └─System
└─Drivers
├─CMSIS
│ ├─Device
│ │ └─ST
│ │ └─STM32MP1xx
│ │ ├─Include
│ │ └─Source
│ │ └─Templates
│ └─Include
└─STM32MP1xx_HAL_Driver
├─Inc
│ └─Legacy
└─Src
2 单片机
选取几个实验复现并总结。
2.1 通用定时器-PWM
设计需求
假设需求为利用定时器产生 PWM,实现 LED 亮度渐变。
基础知识
PWM(Pulse Width Modulation),即脉冲宽度调制。是一种利用微处理器的数字输出来对模拟电路进行控制的技术,广泛应用在测量、通信、功率控制等诸多领域。
PWM 实质就是 GPIO 不断翻转输出高、低电平,这个效果可以写代码控制 GPIO 产生,但这样就会占用CPU,CPU 就不方便做其它事情。MCU 里都有一个定时器,配置好这个定时器,就可以让它去控制 GPIO 自动翻转,无需 CPU 再参与。
硬件设置
开发板有两个LED灯,引脚为PA10、PG8,都支持TIM复用功能。选取PG8,设置其复用功能为TIM2_CH1输出PWM。
1、MCU系统时钟设为209MHz,TIM2时钟设为209MHz。
2、设置PG8引脚复用为TIM_CH1。切换到"Timers"标签,选择“TIM2”,勾选给 Cortex-M4。
在下面的通道 1“Channel1”选择其功能为产生 PWM“PWM Generation CH1”。
再切换到下面的“Parameter Settings”标签,设置分频系数“Perscaler”,这里 MCU 主频为 209MHz,这里为了方便计算,设置为“209-1”分频,这样定时器频率就工作在 209MHZ/209=1MHz。接着设置定时器计数周期“Counter Period”值为“1000-1”,这样定时器计数就会从 0 开始,计数到 1000,完成一次计数周期,然后再从 0 开始,周而往复,此时一个定时器频率为 1MHz/1000=1KHz,即 1 秒 GPIO 翻转 1000 次。
大部分默认即可,通道极性“CH Polarity”可改为“Low”,因为 LED 是低电平亮。在代码里,修改“Pulse”的值,即可修改占空比。比如当“Pulse”值为 500 时,即 0-500 计数期间为低电平 LED 亮,500-1000 计数周期为高电平 LED 灭,LED 从宏观看就是半亮状态。
3、使能 TIM2 中断,这样完成一个计数周期才会进入中断回调函数,在中断回调函数实现修改占空比,不断改变 LED 亮度。
4、框架代码生成。
代码设计
1、在M4工程下面新建DemoDriver文件夹,添加自定义驱动文件,自动生成代码时也不会丢失。
2、DemoDriver加入工程路径。鼠标右键选择“Properties”,接着在“C/C++ General”下选择“Paths and Symbols”,切到“Includes”标签,点击右边的“Add…”, 输入刚才新建的文件夹名“DemoDriver”。再切到源码路径“Source Location”,包含DemoDriver。最后点击应用并关闭窗口“Apply and Close”。
3、编码。
/* driver_tim_pwm.h */
#ifndef __DRIVER_TIM_PWM_H
#define __DRIVER_TIM_PWM_H
#include "main.h"
/*
* 函数名:void TIM_LED3_PWM_Start(void)
* 输入参数:无
* 输出参数:无
* 函数作用:开启定时器
*/
void TIM_LED3_PWM_Start(void);
#endif
/* driver_tim_pwm.c */
#include "driver_tim_pwm.h"
#include "tim.h"
/*
* 函数名:void TIM_LED3_PWM_Start(void)
* 输入参数:无
* 输出参数:无
* 函数作用:开启定时器
*/
void TIM_LED3_PWM_Start(void)
{
if(HAL_TIM_Base_Start_IT(&htim2) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
}
/*
* 函数名:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
* 输入参数:htim->定时器句柄
* 输出参数:无
* 函数作用:每当发生一次周期溢出时间会到此中断回调函数,在此函数内更新占空比
*/
static uint16_t pulse = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
pulse = pulse + 1;
if(pulse>=1000)
{
pulse = 0;
}
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse);
}
}
/* main.c */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "driver_tim_pwm.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
if(IS_ENGINEERING_BOOT_MODE())
{
/* Configure the system clock */
SystemClock_Config();
}
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
TIM_LED3_PWM_Start();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL3.PLLSource = RCC_PLL3SOURCE_HSI;
RCC_OscInitStruct.PLL3.PLLM = 4;
RCC_OscInitStruct.PLL3.PLLN = 26;
RCC_OscInitStruct.PLL3.PLLP = 2;
RCC_OscInitStruct.PLL3.PLLQ = 2;
RCC_OscInitStruct.PLL3.PLLR = 2;
RCC_OscInitStruct.PLL3.PLLRGE = RCC_PLL3IFRANGE_1;
RCC_OscInitStruct.PLL3.PLLFRACV = 1024;
RCC_OscInitStruct.PLL3.PLLMODE = RCC_PLL_FRACTIONAL;
RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** RCC Clock Config
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4
|RCC_CLOCKTYPE_PCLK5;
RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_HSI;
RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1;
RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_PLL3;
RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1;
RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV1;
RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV1;
RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
4、编译、下载、DEBUG。
2.2 ADC-DMA
- 设计需求
假设需求为使用 ADC,获取光敏电阻两端电压值,从而得知外部光照变化。
2.3 CAN通信
- 设计需求
假设需求为使用百问网的 CAN-RS485 互转模块,实现 RS485、CAN 接口互相发送、接收数据。
3 RTOS
4 Linux
- 学习资料:2021韩顺平 一周学会Linux
该课程目标是入门Linux,全面系统掌握Linux知识点,能够在工作中灵活使用,优化系统。不清楚的知识点,先明白怎么做,再去想为什么。
4.1 VMWare与Ubuntu
安装VMWare虚拟机,虚拟机上运行Ubuntu系统。
快速打开终端,Ctrl+Alt+T。
放大终端窗口 Ctrl+Shift+ +。
缩小终端窗口 Ctrl + -。
要想查看某个分区挂载在哪一个目录下,可以执行命令df -h
。
在终端执行ls -al
命令显示当前目录下的所有文件及文件夹的详细信息。
4.2 Linux 常用命令
命令行
命令提示符 book@100ask:~$
- book : 当前登录的用户名
- 100ask :主机名
- ~ :当前所在目录,~表示家目录
- $ : 用户提示符,root管理员显示#,普通用户显示 $
Linux 命令的格式
Linux 命令格式
- []中括号表示,该部分可选,可有可无,需要根据命令的实际需要而添加;
- 命令、选项、参数都以空格分隔,不管几个空格都算一个空格;
- 命令输入完毕后,按回车“Enter”键启动。
Linux 命令示例
绝对路径与相对路径
Linux 下的根目录为“/”,从根目录下出发可以找到任意目录、任意文件。从根目录开始表示目录或文件的方法称为绝对路径。
有时候使用绝对路径太过麻烦 ,可以使用相对路径。 假设当前正位于/home/book 目录下,那么:
./1.txt
表示当前目录下的 1.txt,即 /home/book/1.txt;“.”表示当前目录../book/1.txt
表示当前目录的上一级目录里,book 子目录下的 1.txt"/home/book/.."
就是”/home”目录,”..”表示上一级目录
相对路径中,. 表示当前路径,..表示上一级路径,../..表示上上级路径,以此类推。
目录/文件操作命令
-
显示当前所在路径
- pwd
-
切换路径
- cd 路径
- cd -
- 进入上次目录, 比如先进入 a 目录再进入 b 目录,执行此命令后即回到 a 目录
- cd ~
- 进入家目录
-
创建目录
- mkdir dir0
- mkdir -p dir0/dir1
-
删除一个空目录
- rmdir
-
列出目录内容
- ls
- ls -a 显示当前目录下文件及隐藏文件
- ls -la ”-”和”-a”组合选项,显示所有文件及完整信息
- ls -lh lh表示-human-readable,大小以K/MWG等可读方式列出
- ls
-
复制目录
- cp -r dir1 dir2
- cp -rfd dir_a dir_b
- r:recursive,递归地,即复制所有文件
- f:force,强制覆盖
- d:如果源文件为链接文件,也只是把它作为链接文件复制过去,而不是复制实际文件
- cp -rfd dir_a dir_b
- cp -r dir1 dir2
-
删除文件或目录
- rm file
- rm -rf dir_a
- r:recursive,递归地,即删除所有文件
- f:force,强制删除
-
串联文件的内容并打印出来
- cat file1.txt file2.text
-
修改文件的时间,如果文件不存在则创建空文件
- touch file
...