0 输入信息

1 开发环境

开发工具

  • IDE: STM32CubeIDE
  • 串口调试:MobaXterm
  • 代码编辑:VSCode
  • 交叉编译:gcc-arm-none-eabi
  • 代码调试:openocd、ST-Link

开发流程

  1. 软件安装
  2. 创建模板
    • 创建工作区
    • 新建STM32 Project
    • 芯片选择区输入STM32MP157D,选择TFBGA361
    • 创建项目名称
    • 结合原理图进行引脚设置
    • 菜单栏Project选择Generate Code生成项目程序框架模板
  3. 使用HAL库调用函数接口进行应用开发
  4. 编译上传
    • 选择项目下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

该课程目标是入门Linux,全面系统掌握Linux知识点,能够在工作中灵活使用,优化系统。不清楚的知识点,先明白怎么做,再去想为什么。

锁相环(PLL: Phase-locked loop)

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等可读方式列出
  • 复制目录

    • cp -r dir1 dir2
      • cp -rfd dir_a dir_b
        • r:recursive,递归地,即复制所有文件
        • f:force,强制覆盖
        • d:如果源文件为链接文件,也只是把它作为链接文件复制过去,而不是复制实际文件
  • 删除文件或目录

    • rm file
    • rm -rf dir_a
      • r:recursive,递归地,即删除所有文件
      • f:force,强制删除
  • 串联文件的内容并打印出来

    • cat file1.txt file2.text
  • 修改文件的时间,如果文件不存在则创建空文件

    • touch file
改变文件的权限和属性
查找/搜索命令
压缩/解压缩命令
网络命令
vi 编辑器
其他命令