/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "delay.h"
#include "bsp_printf.h"
#include "bsp_key.h"
#include "string.h"
#include "usmart.h"
#include "usart.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 */
volatile uint8_t rx_done, tx_done;
uint8_t mpudata[128] __attribute__((at(0X20002000)));//定义一个数组
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void led_set(uint8_t sta)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, (GPIO_PinState)sta);
}
/* 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 */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
uart_init(115200); //串口初始化
usmart_dev.init(108); //初始化USMART
delay_init(216);
delay_ms(5000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
/* 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};
/** Configure LSE Drive Capability
*/
HAL_PWR_EnableBkUpAccess();
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
RCC_OscInitStruct.PLL.PLLR = 2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
{
Error_Handler();
}
/** Enables the Clock Security System
*/
HAL_RCC_EnableCSS();
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
/*Configure GPIO pin : PB1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* 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 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f7xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//extern SDRAM_HandleTypeDef hsdram1;
//extern SD_HandleTypeDef hsd1;
//extern QSPI_HandleTypeDef hqspi;
//extern NAND_HandleTypeDef hnand1;
//extern SAI_HandleTypeDef hsai_BlockA1;
//extern SAI_HandleTypeDef hsai_BlockB1;
//extern DMA_HandleTypeDef hdma_sai1_a;
//extern DMA_HandleTypeDef hdma_sai1_b;
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
extern volatile uint8_t rx_done, tx_done;
void led_set(uint8_t sta);
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
定义一些常用的数据类型短关键字
//typedef int32_t s32;
//typedef int16_t s16;
//typedef int8_t s8;
//typedef const int32_t sc32;
//typedef const int16_t sc16;
//typedef const int8_t sc8;
//typedef __IO int32_t vs32;
//typedef __IO int16_t vs16;
//typedef __IO int8_t vs8;
//typedef __I int32_t vsc32;
//typedef __I int16_t vsc16;
//typedef __I int8_t vsc8;
//typedef uint32_t u32;
//typedef uint16_t u16;
//typedef uint8_t u8;
//typedef const uint32_t uc32;
//typedef const uint16_t uc16;
//typedef const uint8_t uc8;
//typedef __IO uint32_t vu32;
//typedef __IO uint16_t vu16;
//typedef __IO uint8_t vu8;
//typedef __I uint32_t vuc32;
//typedef __I uint16_t vuc16;
//typedef __I uint8_t vuc8;
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
#ifndef _USART_H
#define _USART_H
#include "main.h"
#include "stdio.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//串口1初始化
//正点原子@ALIENTEK
//技术论坛:www.openedv.csom
//修改日期:2015/6/23
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 正点原子 2009-2019
//All rights reserved
//********************************************************************************
//V1.0修改说明
//
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
extern uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern uint16_t USART_RX_STA; //接收状态标记
extern UART_HandleTypeDef UART1_Handler; //UART句柄
#define RXBUFFERSIZE 1 //缓存大小
extern uint8_t aRxBuffer[RXBUFFERSIZE];//HAL库USART接收Buffer
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(uint32_t bound);
#endif
#include "usart.h"
//
//如果使用os,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //os 使用
#endif
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//串口1初始化
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2015/6/23
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.0修改说明
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->ISR&0X40)==0);//循环发送,直到发送完毕
USART1->TDR=(uint8_t)ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
uint16_t USART_RX_STA=0; //接收状态标记
uint8_t aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄
//初始化IO 串口1
//bound:波特率
void uart_init(uint32_t bound)
{
//UART 初始化设置
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1
//HAL_UART_Receive_IT(&UART1_Handler, (uint8_t *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}
//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
#if EN_USART1_RX
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收中断
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif
}
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
uint8_t Res;
#if SYSTEM_SUPPORT_OS //使用OS
OSIntEnter();
#endif
if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
HAL_UART_Receive(&UART1_Handler,&Res,1,1000);
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
HAL_UART_IRQHandler(&UART1_Handler);
#if SYSTEM_SUPPORT_OS //使用OS
OSIntExit();
#endif
}
#endif
/****************************************************************************************/
/****************************************************************************************/
/*************************下面程序通过在回调函数中编写中断控制逻辑*********************/
/****************************************************************************************
***************************************************************************************************
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
uint32_t timeout=0;
uint32_t maxDelay=0x1FFFF;
#if SYSTEM_SUPPORT_OS //使用OS
OSIntEnter();
#endif
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
timeout=0;
while (HAL_UART_GetState(&UART1_Handler)!=HAL_UART_STATE_READY)//等待就绪
{
timeout++;超时处理
if(timeout>maxDelay) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&UART1_Handler,(uint8_t *)aRxBuffer, RXBUFFERSIZE)!=HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>maxDelay) break;
}
#if SYSTEM_SUPPORT_OS //使用OS
OSIntExit();
#endif
}
#endif
*/
#ifndef __USMART_H
#define __USMART_H
#include "usmart_str.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//版本:V3.3
//版权所有,盗版必究。
//Copyright(C) 正点原子 2011-2021
//All rights reserved
//********************************************************************************
//升级说明
//V1.4
//增加了对参数为string类型的函数的支持.适用范围大大提高.
//优化了内存占用,静态内存占用为79个字节@10个参数.动态适应数字及字符串长度
//V2.0
//1,修改了list指令,打印函数的完整表达式.
//2,增加了id指令,打印每个函数的入口地址.
//3,修改了参数匹配,支持函数参数的调用(输入入口地址).
//4,增加了函数名长度宏定义.
//V2.1 20110707
//1,增加dec,hex两个指令,用于设置参数显示进制,及执行进制转换.
//注:当dec,hex不带参数的时候,即设定显示参数进制.当后跟参数的时候,即执行进制转换.
//如:"dec 0XFF" 则会将0XFF转为255,由串口返回.
//如:"hex 100" 则会将100转为0X64,由串口返回
//2,新增usmart_get_cmdname函数,用于获取指令名字.
//V2.2 20110726
//1,修正了void类型参数的参数统计错误.
//2,修改数据显示格式默认为16进制.
//V2.3 20110815
//1,去掉了函数名后必须跟"("的限制.
//2,修正了字符串参数中不能有"("的bug.
//3,修改了函数默认显示参数格式的修改方式.
//V2.4 20110905
//1,修改了usmart_get_cmdname函数,增加最大参数长度限制.避免了输入错误参数时的死机现象.
//2,增加USMART_ENTIM2_SCAN宏定义,用于配置是否使用TIM2定时执行scan函数.
//V2.5 20110930
//1,修改usmart_init函数为void usmart_init(uint8_t sysclk),可以根据系统频率自动设定扫描时间.(固定100ms)
//2,去掉了usmart_init函数中的uart_init函数,串口初始化必须在外部初始化,方便用户自行管理.
//V2.6 20111009
//1,增加了read_addr和write_addr两个函数.可以利用这两个函数读写内部任意地址(必须是有效地址).更加方便调试.
//2,read_addr和write_addr两个函数可以通过设置USMART_USE_WRFUNS为来使能和关闭.
//3,修改了usmart_strcmp,使其规范化.
//V2.7 20111024
//1,修正了返回值16进制显示时不换行的bug.
//2,增加了函数是否有返回值的判断,如果没有返回值,则不会显示.有返回值时才显示其返回值.
//V2.8 20111116
//1,修正了list等不带参数的指令发送后可能导致死机的bug.
//V2.9 20120917
//1,修改了形如:void*xxx(void)类型函数不能识别的bug。
//V3.0 20130425
//1,新增了字符串参数对转义符的支持。
//V3.1 20131120
//1,增加runtime系统指令,可以用于统计函数执行时间.
//用法:
//发送:runtime 1 ,则开启函数执行时间统计功能
//发送:runtime 0 ,则关闭函数执行时间统计功能
///runtime统计功能,必须设置:USMART_ENTIMX_SCAN 为1,才可以使用!!
//V3.2 20140828
//1,修改usmart_get_aparm函数,加入+/-符号的支持
//2,修改usmart_str2num函数,支持负数转换
//V3.3 20160506
//修正usmart_exe函数在USMART_ENTIMX_SCAN为0的时候,报错的bug
/
//USMART资源占用情况@MDK 3.80A@2.0版本:
//FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
//SRAM:72字节(最少的情况下)
//SRAM计算公式: SRAM=PARM_LEN+72-4 其中PARM_LEN必须大于等于4.
//应该保证堆栈不小于100个字节.
用户配置参数
#define MAX_FNAME_LEN 30 //函数名最大长度,应该设置为不小于最长函数名的长度。
#define MAX_PARM 10 //最大为10个参数 ,修改此参数,必须修改usmart_exe与之对应.
#define PARM_LEN 200 //所有参数之和的长度不超过PARM_LEN个字节,注意串口接收部分要与之对应(不小于PARM_LEN)
#define USMART_ENTIMX_SCAN 1 //使用TIM的定时中断来扫描SCAN函数,如果设置为0,需要自己实现隔一段时间扫描一次scan函数.
//注意:如果要用runtime统计功能,必须设置USMART_ENTIMX_SCAN为1!!!!
#define USMART_USE_HELP 1 //使用帮助,该值设为0,可以节省近700个字节,但是将导致无法显示帮助信息。
#define USMART_USE_WRFUNS 1 //使用读写函数,使能这里,可以读取任何地址的值,还可以写寄存器的值.
///END///
#define USMART_OK 0 //无错误
#define USMART_FUNCERR 1 //函数错误
#define USMART_PARMERR 2 //参数错误
#define USMART_PARMOVER 3 //参数溢出
#define USMART_NOFUNCFIND 4 //未找到匹配函数
#define SP_TYPE_DEC 0 //10进制参数显示
#define SP_TYPE_HEX 1 //16进制参数显示
//函数名列表
struct _m_usmart_nametab
{
void* func; //函数指针
const uint8_t* name; //函数名(查找串)
};
//usmart控制管理器
struct _m_usmart_dev
{
struct _m_usmart_nametab *funs; //函数名指针
void (*init)(uint8_t); //初始化
uint8_t (*cmd_rec)(uint8_t*str); //识别函数名及参数
void (*exe)(void); //执行
void (*scan)(void); //扫描
uint8_t fnum; //函数数量
uint8_t pnum; //参数数量
uint8_t id; //函数id
uint8_t sptype; //参数显示类型(非字符串参数):0,10进制;1,16进制;
uint16_t parmtype; //参数的类型
uint8_t plentbl[MAX_PARM]; //每个参数的长度暂存表
uint8_t parm[PARM_LEN]; //函数的参数
uint8_t runtimeflag; //0,不统计函数执行时间;1,统计函数执行时间,注意:此功能必须在USMART_ENTIMX_SCAN使能的时候,才有用
uint32_t runtime; //运行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms
};
extern struct _m_usmart_nametab usmart_nametab[]; //在usmart_config.c里面定义
extern struct _m_usmart_dev usmart_dev; //在usmart_config.c里面定义
void usmart_init(uint8_t sysclk);//初始化
uint8_t usmart_cmd_rec(uint8_t*str); //识别
void usmart_exe(void); //执行
void usmart_scan(void); //扫描
uint32_t read_addr(uint32_t addr); //读取指定地址的值
void write_addr(uint32_t addr,uint32_t val);//在指定地址写入指定的值
uint32_t usmart_get_runtime(void); //获取运行时间
void usmart_reset_runtime(void);//复位运行时间
#endif
#include "usmart_str.h"
#include "usmart.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//版本:V3.3
//版权所有,盗版必究。
//Copyright(C) 正点原子 2011-2021
//All rights reserved
//********************************************************************************
//升级说明
//V1.4
//增加了对参数为string类型的函数的支持.适用范围大大提高.
//优化了内存占用,静态内存占用为79个字节@10个参数.动态适应数字及字符串长度
//V2.0
//1,修改了list指令,打印函数的完整表达式.
//2,增加了id指令,打印每个函数的入口地址.
//3,修改了参数匹配,支持函数参数的调用(输入入口地址).
//4,增加了函数名长度宏定义.
//V2.1 20110707
//1,增加dec,hex两个指令,用于设置参数显示进制,及执行进制转换.
//注:当dec,hex不带参数的时候,即设定显示参数进制.当后跟参数的时候,即执行进制转换.
//如:"dec 0XFF" 则会将0XFF转为255,由串口返回.
//如:"hex 100" 则会将100转为0X64,由串口返回
//2,新增usmart_get_cmdname函数,用于获取指令名字.
//V2.2 20110726
//1,修正了void类型参数的参数统计错误.
//2,修改数据显示格式默认为16进制.
//V2.3 20110815
//1,去掉了函数名后必须跟"("的限制.
//2,修正了字符串参数中不能有"("的bug.
//3,修改了函数默认显示参数格式的修改方式.
//V2.4 20110905
//1,修改了usmart_get_cmdname函数,增加最大参数长度限制.避免了输入错误参数时的死机现象.
//2,增加USMART_ENTIM2_SCAN宏定义,用于配置是否使用TIM2定时执行scan函数.
//V2.5 20110930
//1,修改usmart_init函数为void usmart_init(uint8_t sysclk),可以根据系统频率自动设定扫描时间.(固定100ms)
//2,去掉了usmart_init函数中的uart_init函数,串口初始化必须在外部初始化,方便用户自行管理.
//V2.6 20111009
//1,增加了read_addr和write_addr两个函数.可以利用这两个函数读写内部任意地址(必须是有效地址).更加方便调试.
//2,read_addr和write_addr两个函数可以通过设置USMART_USE_WRFUNS为来使能和关闭.
//3,修改了usmart_strcmp,使其规范化.
//V2.7 20111024
//1,修正了返回值16进制显示时不换行的bug.
//2,增加了函数是否有返回值的判断,如果没有返回值,则不会显示.有返回值时才显示其返回值.
//V2.8 20111116
//1,修正了list等不带参数的指令发送后可能导致死机的bug.
//V2.9 20120917
//1,修改了形如:void*xxx(void)类型函数不能识别的bug。
//V3.0 20130425
//1,新增了字符串参数对转义符的支持。
//V3.1 20131120
//1,增加runtime系统指令,可以用于统计函数执行时间.
//用法:
//发送:runtime 1 ,则开启函数执行时间统计功能
//发送:runtime 0 ,则关闭函数执行时间统计功能
///runtime统计功能,必须设置:USMART_ENTIMX_SCAN 为1,才可以使用!!
//V3.2 20140828
//1,修改usmart_get_aparm函数,加入+/-符号的支持
//2,修改usmart_str2num函数,支持负数转换
//V3.3 20160506
//修正usmart_exe函数在USMART_ENTIMX_SCAN为0的时候,报错的bug
/
//对比字符串str1和str2
//*str1:字符串1指针
//*str2:字符串2指针
//返回值:0,相等;1,不相等;
uint8_t usmart_strcmp(uint8_t *str1,uint8_t *str2)
{
while(1)
{
if(*str1!=*str2)return 1;//不相等
if(*str1=='\0')break;//对比完成了.
str1++;
str2++;
}
return 0;//两个字符串相等
}
//把str1的内容copy到str2
//*str1:字符串1指针
//*str2:字符串2指针
void usmart_strcopy(uint8_t*str1,uint8_t *str2)
{
while(1)
{
*str2=*str1; //拷贝
if(*str1=='\0')break;//拷贝完成了.
str1++;
str2++;
}
}
//得到字符串的长度(字节)
//*str:字符串指针
//返回值:字符串的长度
uint8_t usmart_strlen(uint8_t*str)
{
uint8_t len=0;
while(1)
{
if(*str=='\0')break;//拷贝完成了.
len++;
str++;
}
return len;
}
//m^n函数
//返回值:m^n次方
uint32_t usmart_pow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
//把字符串转为数字
//支持16进制转换,但是16进制字母必须是大写的,且格式为以0X开头的.
//支持负数
//*str:数字字符串指针
//*res:转换完的结果存放地址.
//返回值:0,成功转换完成.其他,错误代码.
//1,数据格式错误.2,16进制位数为0.3,起始格式错误.4,十进制位数为0.
uint8_t usmart_str2num(uint8_t*str,uint32_t *res)
{
uint32_t t;
int tnum;
uint8_t bnum=0; //数字的位数
uint8_t *p;
uint8_t hexdec=10; //默认为十进制数据
uint8_t flag=0; //0,没有符号标记;1,表示正数;2,表示负数.
p=str;
*res=0;//清零.
while(1)
{
if((*p<='9'&&*p>='0')||((*str=='-'||*str=='+')&&bnum==0)||(*p<='F'&&*p>='A')||(*p=='X'&&bnum==1))//参数合法
{
if(*p>='A')hexdec=16; //字符串中存在字母,为16进制格式.
if(*str=='-'){flag=2;str+=1;}//偏移掉符号
else if(*str=='+'){flag=1;str+=1;}//偏移掉符号
else bnum++; //位数增加.
}else if(*p=='\0')break; //碰到结束符,退出.
else return 1; //不全是十进制或者16进制数据.
p++;
}
p=str; //重新定位到字符串开始的地址.
if(hexdec==16) //16进制数据
{
if(bnum<3)return 2; //位数小于3,直接退出.因为0X就占了2个,如果0X后面不跟数据,则该数据非法.
if(*p=='0' && (*(p+1)=='X'))//必须以'0X'开头.
{
p+=2; //偏移到数据起始地址.
bnum-=2;//减去偏移量
}else return 3;//起始头的格式不对
}else if(bnum==0)return 4;//位数为0,直接退出.
while(1)
{
if(bnum)bnum--;
if(*p<='9'&&*p>='0')t=*p-'0'; //得到数字的值
else t=*p-'A'+10; //得到A~F对应的值
*res+=t*usmart_pow(hexdec,bnum);
p++;
if(*p=='\0')break;//数据都查完了.
}
if(flag==2)//是负数?
{
tnum=-*res;
*res=tnum;
}
return 0;//成功转换
}
//得到指令名
//*str:源字符串
//*cmdname:指令名
//*nlen:指令名长度
//maxlen:最大长度(做限制,指令不可能太长的)
//返回值:0,成功;其他,失败.
uint8_t usmart_get_cmdname(uint8_t*str,uint8_t*cmdname,uint8_t *nlen,uint8_t maxlen)
{
*nlen=0;
while(*str!=' '&&*str!='\0') //找到空格或者结束符则认为结束了
{
*cmdname=*str;
str++;
cmdname++;
(*nlen)++;//统计命令长度
if(*nlen>=maxlen)return 1;//错误的指令
}
*cmdname='\0';//加入结束符
return 0;//正常返回
}
//获取下一个字符(当中间有很多空格的时候,此函数直接忽略空格,找到空格之后的第一个字符)
//str:字符串指针
//返回值:下一个字符
uint8_t usmart_search_nextc(uint8_t* str)
{
str++;
while(*str==' '&&str!='\0')str++;
return *str;
}
//从str中得到函数名
//*str:源字符串指针
//*fname:获取到的函数名字指针
//*pnum:函数的参数个数
//*rval:是否需要显示返回值(0,不需要;1,需要)
//返回值:0,成功;其他,错误代码.
uint8_t usmart_get_fname(uint8_t*str,uint8_t*fname,uint8_t *pnum,uint8_t *rval)
{
uint8_t res;
uint8_t fover=0; //括号深度
uint8_t *strtemp;
uint8_t offset=0;
uint8_t parmnum=0;
uint8_t temp=1;
uint8_t fpname[6];//void+X+'/0'
uint8_t fplcnt=0; //第一个参数的长度计数器
uint8_t pcnt=0; //参数计数器
uint8_t nchar;
//判断函数是否有返回值
strtemp=str;
while(*strtemp!='\0')//没有结束
{
if(*strtemp!=' '&&(pcnt&0X7F)<5)//最多记录5个字符
{
if(pcnt==0)pcnt|=0X80;//置位最高位,标记开始接收返回值类型
if(((pcnt&0x7f)==4)&&(*strtemp!='*'))break;//最后一个字符,必须是*
fpname[pcnt&0x7f]=*strtemp;//记录函数的返回值类型
pcnt++;
}else if(pcnt==0X85)break;
strtemp++;
}
if(pcnt)//接收完了
{
fpname[pcnt&0x7f]='\0';//加入结束符
if(usmart_strcmp(fpname,"void")==0)*rval=0;//不需要返回值
else *rval=1; //需要返回值
pcnt=0;
}
res=0;
strtemp=str;
while(*strtemp!='('&&*strtemp!='\0') //此代码找到函数名的真正起始位置
{
strtemp++;
res++;
if(*strtemp==' '||*strtemp=='*')
{
nchar=usmart_search_nextc(strtemp); //获取下一个字符
if(nchar!='('&&nchar!='*')offset=res; //跳过空格和*号
}
}
strtemp=str;
if(offset)strtemp+=offset+1;//跳到函数名开始的地方
res=0;
nchar=0;//是否正在字符串里面的标志,0,不在字符串;1,在字符串;
while(1)
{
if(*strtemp==0)
{
res=USMART_FUNCERR;//函数错误
break;
}else if(*strtemp=='('&&nchar==0)fover++;//括号深度增加一级
else if(*strtemp==')'&&nchar==0)
{
if(fover)fover--;
else res=USMART_FUNCERR;//错误结束,没收到'('
if(fover==0)break;//到末尾了,退出
}else if(*strtemp=='"')nchar=!nchar;
if(fover==0)//函数名还没接收完
{
if(*strtemp!=' ')//空格不属于函数名
{
*fname=*strtemp;//得到函数名
fname++;
}
}else //已经接受完了函数名了.
{
if(*strtemp==',')
{
temp=1; //使能增加一个参数
pcnt++;
}else if(*strtemp!=' '&&*strtemp!='(')
{
if(pcnt==0&&fplcnt<5) //当第一个参数来时,为了避免统计void类型的参数,必须做判断.
{
fpname[fplcnt]=*strtemp;//记录参数特征.
fplcnt++;
}
temp++; //得到有效参数(非空格)
}
if(fover==1&&temp==2)
{
temp++; //防止重复增加
parmnum++; //参数增加一个
}
}
strtemp++;
}
if(parmnum==1)//只有1个参数.
{
fpname[fplcnt]='\0';//加入结束符
if(usmart_strcmp(fpname,"void")==0)parmnum=0;//参数为void,表示没有参数.
}
*pnum=parmnum; //记录参数个数
*fname='\0'; //加入结束符
return res; //返回执行结果
}
//从str中得到一个函数的参数
//*str:源字符串指针
//*fparm:参数字符串指针
//*ptype:参数类型 0,数字;1,字符串;0XFF,参数错误
//返回值:0,已经无参数了;其他,下一个参数的偏移量.
uint8_t usmart_get_aparm(uint8_t *str,uint8_t *fparm,uint8_t *ptype)
{
uint8_t i=0;
uint8_t enout=0;
uint8_t type=0;//默认是数字
uint8_t string=0; //标记str是否正在读
while(1)
{
if(*str==','&& string==0)enout=1; //暂缓立即退出,目的是寻找下一个参数的起始地址
if((*str==')'||*str=='\0')&&string==0)break;//立即退出标识符
if(type==0)//默认是数字的
{
if((*str>='0' && *str<='9')||*str=='-'||*str=='+'||(*str>='a' && *str<='f')||(*str>='A' && *str<='F')||*str=='X'||*str=='x')//数字串检测
{
if(enout)break; //找到了下一个参数,直接退出.
if(*str>='a')*fparm=*str-0X20; //小写转换为大写
else *fparm=*str; //小写或者数字保持不变
fparm++;
}else if(*str=='"')//找到字符串的开始标志
{
if(enout)break;//找到,后才找到",认为结束了.
type=1;
string=1;//登记STRING 正在读了
}else if(*str!=' '&&*str!=',')//发现非法字符,参数错误
{
type=0XFF;
break;
}
}else//string类
{
if(*str=='"')string=0;
if(enout)break; //找到了下一个参数,直接退出.
if(string) //字符串正在读
{
if(*str=='\\') //遇到转义符(不复制转义符)
{
str++; //偏移到转义符后面的字符,不管什么字符,直接COPY
i++;
}
*fparm=*str; //小写或者数字保持不变
fparm++;
}
}
i++;//偏移量增加
str++;
}
*fparm='\0'; //加入结束符
*ptype=type; //返回参数类型
return i; //返回参数长度
}
//得到指定参数的起始地址
//num:第num个参数,范围0~9.
//返回值:该参数的起始地址
uint8_t usmart_get_parmpos(uint8_t num)
{
uint8_t temp=0;
uint8_t i;
for(i=0;i<num;i++)temp+=usmart_dev.plentbl[i];
return temp;
}
//从str中得到函数参数
//str:源字符串;
//parn:参数的多少.0表示无参数 void类型
//返回值:0,成功;其他,错误代码.
uint8_t usmart_get_fparam(uint8_t*str,uint8_t *parn)
{
uint8_t i,type;
uint32_t res;
uint8_t n=0;
uint8_t len;
uint8_t tstr[PARM_LEN+1];//字节长度的缓存,最多可以存放PARM_LEN个字符的字符串
for(i=0;i<MAX_PARM;i++)usmart_dev.plentbl[i]=0;//清空参数长度表
while(*str!='(')//偏移到参数开始的地方
{
str++;
if(*str=='\0')return USMART_FUNCERR;//遇到结束符了
}
str++;//偏移到"("之后的第一个字节
while(1)
{
i=usmart_get_aparm(str,tstr,&type); //得到第一个参数
str+=i; //偏移
switch(type)
{
case 0: //数字
if(tstr[0]!='\0') //接收到的参数有效
{
i=usmart_str2num(tstr,&res); //记录该参数
if(i)return USMART_PARMERR; //参数错误.
*(uint32_t*)(usmart_dev.parm+usmart_get_parmpos(n))=res;//记录转换成功的结果.
usmart_dev.parmtype&=~(1<<n); //标记数字
usmart_dev.plentbl[n]=4; //该参数的长度为4
n++; //参数增加
if(n>MAX_PARM)return USMART_PARMOVER;//参数太多
}
break;
case 1://字符串
len=usmart_strlen(tstr)+1; //包含了结束符'\0'
usmart_strcopy(tstr,&usmart_dev.parm[usmart_get_parmpos(n)]);//拷贝tstr数据到usmart_dev.parm[n]
usmart_dev.parmtype|=1<<n; //标记字符串
usmart_dev.plentbl[n]=len; //该参数的长度为len
n++;
if(n>MAX_PARM)return USMART_PARMOVER;//参数太多
break;
case 0XFF://错误
return USMART_PARMERR;//参数错误
}
if(*str==')'||*str=='\0')break;//查到结束标志了.
}
*parn=n; //记录参数的个数
return USMART_OK;//正确得到了参数
}
#include "usmart.h"
#include "usmart_str.h"
用户配置区///
//这下面要包含所用到的函数所申明的头文件(用户自己添加)
#include "delay.h"
#include "main.h"
//#include "lcd.h"
//#include "sdram.h"
//#include "ltdc.h"
//extern void led_set(uint8_t sta);
//extern void test_fun(void(*ledset)(uint8_t),uint8_t sta);
//函数名列表初始化(用户自己添加)
//用户直接在这里输入要执行的函数名及其查找串
struct _m_usmart_nametab usmart_nametab[]=
{
#if USMART_USE_WRFUNS==1 //如果使能了读写操作
(void*)read_addr,"uint32_t read_addr(uint32_t addr)",
(void*)write_addr,"void write_addr(uint32_t addr,uint32_t val)",
#endif
(void*)delay_ms,"void delay_ms(uint16_t nms)",
(void*)delay_us,"void delay_us(uint32_t nus)",
// (void*)LCD_Clear,"void LCD_Clear(uint16_t Color)",
// (void*)LCD_Fill,"void LCD_Fill(uint16_t xsta,uint16_t ysta,uint16_t xend,uint16_t yend,uint16_t color)",
// (void*)LCD_DrawLine,"void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)",
// (void*)LCD_DrawRectangle,"void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)",
// (void*)LCD_Draw_Circle,"void Draw_Circle(uint16_t x0,uint16_t y0,uint8_t r)",
// (void*)LCD_ShowNum,"void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size)",
// (void*)LCD_ShowString,"void LCD_ShowString(uint16_t x,uint16_t y,uint16_t width,uint16_t height,uint8_t size,uint8_t *p)",
// (void*)LCD_Fast_DrawPoint,"void LCD_Fast_DrawPoint(uint16_t x,uint16_t y,uint16_t color)",
// (void*)LCD_ReadPoint,"uint16_t LCD_ReadPoint(uint16_t x,uint16_t y)",
// (void*)LCD_Display_Dir,"void LCD_Display_Dir(uint8_t dir)",
// (void*)LCD_ShowxNum,"void LCD_ShowxNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size,uint8_t mode)",
// (void*)LTDC_Switch,"void LTDC_Switch(uint8_t sw)",
// (void*)LTDC_Layer_Switch,"void LTDC_Layer_Switch(uint8_t layerx,uint8_t sw)",
// (void*)LTDC_Select_Layer,"void LTDC_Select_Layer(uint8_t layerx)",
// (void*)LTDC_Display_Dir,"void LTDC_Display_Dir(uint8_t dir)",
// (void*)LTDC_Draw_Point,"void LTDC_Draw_Point(uint16_t x,uint16_t y,uint32_t color)",
// (void*)LTDC_Read_Point,"uint32_t LTDC_Read_Point(uint16_t x,uint16_t y)",
// (void*)LTDC_Fill,"void LTDC_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint32_t color)",
// (void*)LTDC_Layer_Window_Config,"void LTDC_Layer_Window_Config(uint8_t layerx,uint16_t sx,uint16_t sy,uint16_t width,uint16_t height)",
// (void*)LTDC_Clear,"void LTDC_Clear(uint32_t color)",
(void*)led_set,"void led_set(uint8_t sta)",
// (void*)test_fun,"void test_fun(void(*ledset)(uint8_t),uint8_t sta)",
};
///END///
/
//函数控制管理器初始化
//得到各个受控函数的名字
//得到函数总数量
struct _m_usmart_dev usmart_dev=
{
usmart_nametab,
usmart_init,
usmart_cmd_rec,
usmart_exe,
usmart_scan,
sizeof(usmart_nametab)/sizeof(struct _m_usmart_nametab),//函数数量
0, //参数数量
0, //函数ID
1, //参数显示类型,0,10进制;1,16进制
0, //参数类型.bitx:,0,数字;1,字符串
0, //每个参数的长度暂存表,需要MAX_PARM个0初始化
0, //函数的参数,需要PARM_LEN个0初始化
};
#ifndef __USMART_H
#define __USMART_H
#include "usmart_str.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//版本:V3.3
//版权所有,盗版必究。
//Copyright(C) 正点原子 2011-2021
//All rights reserved
//********************************************************************************
//升级说明
//V1.4
//增加了对参数为string类型的函数的支持.适用范围大大提高.
//优化了内存占用,静态内存占用为79个字节@10个参数.动态适应数字及字符串长度
//V2.0
//1,修改了list指令,打印函数的完整表达式.
//2,增加了id指令,打印每个函数的入口地址.
//3,修改了参数匹配,支持函数参数的调用(输入入口地址).
//4,增加了函数名长度宏定义.
//V2.1 20110707
//1,增加dec,hex两个指令,用于设置参数显示进制,及执行进制转换.
//注:当dec,hex不带参数的时候,即设定显示参数进制.当后跟参数的时候,即执行进制转换.
//如:"dec 0XFF" 则会将0XFF转为255,由串口返回.
//如:"hex 100" 则会将100转为0X64,由串口返回
//2,新增usmart_get_cmdname函数,用于获取指令名字.
//V2.2 20110726
//1,修正了void类型参数的参数统计错误.
//2,修改数据显示格式默认为16进制.
//V2.3 20110815
//1,去掉了函数名后必须跟"("的限制.
//2,修正了字符串参数中不能有"("的bug.
//3,修改了函数默认显示参数格式的修改方式.
//V2.4 20110905
//1,修改了usmart_get_cmdname函数,增加最大参数长度限制.避免了输入错误参数时的死机现象.
//2,增加USMART_ENTIM2_SCAN宏定义,用于配置是否使用TIM2定时执行scan函数.
//V2.5 20110930
//1,修改usmart_init函数为void usmart_init(uint8_t sysclk),可以根据系统频率自动设定扫描时间.(固定100ms)
//2,去掉了usmart_init函数中的uart_init函数,串口初始化必须在外部初始化,方便用户自行管理.
//V2.6 20111009
//1,增加了read_addr和write_addr两个函数.可以利用这两个函数读写内部任意地址(必须是有效地址).更加方便调试.
//2,read_addr和write_addr两个函数可以通过设置USMART_USE_WRFUNS为来使能和关闭.
//3,修改了usmart_strcmp,使其规范化.
//V2.7 20111024
//1,修正了返回值16进制显示时不换行的bug.
//2,增加了函数是否有返回值的判断,如果没有返回值,则不会显示.有返回值时才显示其返回值.
//V2.8 20111116
//1,修正了list等不带参数的指令发送后可能导致死机的bug.
//V2.9 20120917
//1,修改了形如:void*xxx(void)类型函数不能识别的bug。
//V3.0 20130425
//1,新增了字符串参数对转义符的支持。
//V3.1 20131120
//1,增加runtime系统指令,可以用于统计函数执行时间.
//用法:
//发送:runtime 1 ,则开启函数执行时间统计功能
//发送:runtime 0 ,则关闭函数执行时间统计功能
///runtime统计功能,必须设置:USMART_ENTIMX_SCAN 为1,才可以使用!!
//V3.2 20140828
//1,修改usmart_get_aparm函数,加入+/-符号的支持
//2,修改usmart_str2num函数,支持负数转换
//V3.3 20160506
//修正usmart_exe函数在USMART_ENTIMX_SCAN为0的时候,报错的bug
/
//USMART资源占用情况@MDK 3.80A@2.0版本:
//FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
//SRAM:72字节(最少的情况下)
//SRAM计算公式: SRAM=PARM_LEN+72-4 其中PARM_LEN必须大于等于4.
//应该保证堆栈不小于100个字节.
用户配置参数
#define MAX_FNAME_LEN 30 //函数名最大长度,应该设置为不小于最长函数名的长度。
#define MAX_PARM 10 //最大为10个参数 ,修改此参数,必须修改usmart_exe与之对应.
#define PARM_LEN 200 //所有参数之和的长度不超过PARM_LEN个字节,注意串口接收部分要与之对应(不小于PARM_LEN)
#define USMART_ENTIMX_SCAN 1 //使用TIM的定时中断来扫描SCAN函数,如果设置为0,需要自己实现隔一段时间扫描一次scan函数.
//注意:如果要用runtime统计功能,必须设置USMART_ENTIMX_SCAN为1!!!!
#define USMART_USE_HELP 1 //使用帮助,该值设为0,可以节省近700个字节,但是将导致无法显示帮助信息。
#define USMART_USE_WRFUNS 1 //使用读写函数,使能这里,可以读取任何地址的值,还可以写寄存器的值.
///END///
#define USMART_OK 0 //无错误
#define USMART_FUNCERR 1 //函数错误
#define USMART_PARMERR 2 //参数错误
#define USMART_PARMOVER 3 //参数溢出
#define USMART_NOFUNCFIND 4 //未找到匹配函数
#define SP_TYPE_DEC 0 //10进制参数显示
#define SP_TYPE_HEX 1 //16进制参数显示
//函数名列表
struct _m_usmart_nametab
{
void* func; //函数指针
const uint8_t* name; //函数名(查找串)
};
//usmart控制管理器
struct _m_usmart_dev
{
struct _m_usmart_nametab *funs; //函数名指针
void (*init)(uint8_t); //初始化
uint8_t (*cmd_rec)(uint8_t*str); //识别函数名及参数
void (*exe)(void); //执行
void (*scan)(void); //扫描
uint8_t fnum; //函数数量
uint8_t pnum; //参数数量
uint8_t id; //函数id
uint8_t sptype; //参数显示类型(非字符串参数):0,10进制;1,16进制;
uint16_t parmtype; //参数的类型
uint8_t plentbl[MAX_PARM]; //每个参数的长度暂存表
uint8_t parm[PARM_LEN]; //函数的参数
uint8_t runtimeflag; //0,不统计函数执行时间;1,统计函数执行时间,注意:此功能必须在USMART_ENTIMX_SCAN使能的时候,才有用
uint32_t runtime; //运行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms
};
extern struct _m_usmart_nametab usmart_nametab[]; //在usmart_config.c里面定义
extern struct _m_usmart_dev usmart_dev; //在usmart_config.c里面定义
void usmart_init(uint8_t sysclk);//初始化
uint8_t usmart_cmd_rec(uint8_t*str); //识别
void usmart_exe(void); //执行
void usmart_scan(void); //扫描
uint32_t read_addr(uint32_t addr); //读取指定地址的值
void write_addr(uint32_t addr,uint32_t val);//在指定地址写入指定的值
uint32_t usmart_get_runtime(void); //获取运行时间
void usmart_reset_runtime(void);//复位运行时间
#endif
#include "usmart.h"
#include "usart.h"
#include "main.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//版本:V3.1
//版权所有,盗版必究。
//Copyright(C) 正点原子 2011-2021
//All rights reserved
//********************************************************************************
//升级说明
//V1.4
//增加了对参数为string类型的函数的支持.适用范围大大提高.
//优化了内存占用,静态内存占用为79个字节@10个参数.动态适应数字及字符串长度
//V2.0
//1,修改了list指令,打印函数的完整表达式.
//2,增加了id指令,打印每个函数的入口地址.
//3,修改了参数匹配,支持函数参数的调用(输入入口地址).
//4,增加了函数名长度宏定义.
//V2.1 20110707
//1,增加dec,hex两个指令,用于设置参数显示进制,及执行进制转换.
//注:当dec,hex不带参数的时候,即设定显示参数进制.当后跟参数的时候,即执行进制转换.
//如:"dec 0XFF" 则会将0XFF转为255,由串口返回.
//如:"hex 100" 则会将100转为0X64,由串口返回
//2,新增usmart_get_cmdname函数,用于获取指令名字.
//V2.2 20110726
//1,修正了void类型参数的参数统计错误.
//2,修改数据显示格式默认为16进制.
//V2.3 20110815
//1,去掉了函数名后必须跟"("的限制.
//2,修正了字符串参数中不能有"("的bug.
//3,修改了函数默认显示参数格式的修改方式.
//V2.4 20110905
//1,修改了usmart_get_cmdname函数,增加最大参数长度限制.避免了输入错误参数时的死机现象.
//2,增加USMART_ENTIM2_SCAN宏定义,用于配置是否使用TIM2定时执行scan函数.
//V2.5 20110930
//1,修改usmart_init函数为void usmart_init(uint8_t sysclk),可以根据系统频率自动设定扫描时间.(固定100ms)
//2,去掉了usmart_init函数中的uart_init函数,串口初始化必须在外部初始化,方便用户自行管理.
//V2.6 20111009
//1,增加了read_addr和write_addr两个函数.可以利用这两个函数读写内部任意地址(必须是有效地址).更加方便调试.
//2,read_addr和write_addr两个函数可以通过设置USMART_USE_WRFUNS为来使能和关闭.
//3,修改了usmart_strcmp,使其规范化.
//V2.7 20111024
//1,修正了返回值16进制显示时不换行的bug.
//2,增加了函数是否有返回值的判断,如果没有返回值,则不会显示.有返回值时才显示其返回值.
//V2.8 20111116
//1,修正了list等不带参数的指令发送后可能导致死机的bug.
//V2.9 20120917
//1,修改了形如:void*xxx(void)类型函数不能识别的bug。
//V3.0 20130425
//1,新增了字符串参数对转义符的支持。
//V3.1 20131120
//1,增加runtime系统指令,可以用于统计函数执行时间.
//用法:
//发送:runtime 1 ,则开启函数执行时间统计功能
//发送:runtime 0 ,则关闭函数执行时间统计功能
///runtime统计功能,必须设置:USMART_ENTIMX_SCAN 为1,才可以使用!!
//V3.2 20140828
//1,修改usmart_get_aparm函数,加入+/-符号的支持
//2,修改usmart_str2num函数,支持负数转换
//V3.3 20160506
//修正usmart_exe函数在USMART_ENTIMX_SCAN为0的时候,报错的bug
/
TIM_HandleTypeDef TIM4_Handler; //定时器句柄
//系统命令
uint8_t *sys_cmd_tab[]=
{
"?",
"help",
"list",
"id",
"hex",
"dec",
"runtime",
};
//处理系统指令
//0,成功处理;其他,错误代码;
uint8_t usmart_sys_cmd_exe(uint8_t *str)
{
uint8_t i;
uint8_t sfname[MAX_FNAME_LEN];//存放本地函数名
uint8_t pnum;
uint8_t rval;
uint32_t res;
res=usmart_get_cmdname(str,sfname,&i,MAX_FNAME_LEN);//得到指令及指令长度
if(res)return USMART_FUNCERR;//错误的指令
str+=i;
for(i=0;i<sizeof(sys_cmd_tab)/4;i++)//支持的系统指令
{
if(usmart_strcmp(sfname,sys_cmd_tab[i])==0)break;
}
switch(i)
{
case 0:
case 1://帮助指令
printf("\r\n");
#if USMART_USE_HELP
printf("------------------------USMART V3.3------------------------ \r\n");
printf(" USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 \r\n");
printf("它,你可以通过串口助手调用程序里面的任何函数,并执行.因此,你可\r\n");
printf("以随意更改函数的输入参数(支持数字(10/16进制,支持负数)、字符串\r\n"),
printf("、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持\r\n"),
printf("函数返回值显示.支持参数显示进制设置功能,支持进制转换功能.\r\n");
printf("技术支持:www.openedv.com\r\n");
printf("USMART有7个系统命令(必须小写):\r\n");
printf("?: 获取帮助信息\r\n");
printf("help: 获取帮助信息\r\n");
printf("list: 可用的函数列表\r\n\n");
printf("id: 可用函数的ID列表\r\n\n");
printf("hex: 参数16进制显示,后跟空格+数字即执行进制转换\r\n\n");
printf("dec: 参数10进制显示,后跟空格+数字即执行进制转换\r\n\n");
printf("runtime:1,开启函数运行计时;0,关闭函数运行计时;\r\n\n");
printf("请按照程序编写格式输入函数名及参数并以回车键结束.\r\n");
printf("--------------------------ALIENTEK------------------------- \r\n");
#else
printf("指令失效\r\n");
#endif
break;
case 2://查询指令
printf("\r\n");
printf("-------------------------函数清单--------------------------- \r\n");
for(i=0;i<usmart_dev.fnum;i++)printf("%s\r\n",usmart_dev.funs[i].name);
printf("\r\n");
break;
case 3://查询ID
printf("\r\n");
printf("-------------------------函数 ID --------------------------- \r\n");
for(i=0;i<usmart_dev.fnum;i++)
{
usmart_get_fname((uint8_t*)usmart_dev.funs[i].name,sfname,&pnum,&rval);//得到本地函数名
printf("%s id is:\r\n0X%08X\r\n",sfname,usmart_dev.funs[i].func); //显示ID
}
printf("\r\n");
break;
case 4://hex指令
printf("\r\n");
usmart_get_aparm(str,sfname,&i);
if(i==0)//参数正常
{
i=usmart_str2num(sfname,&res); //记录该参数
if(i==0) //进制转换功能
{
printf("HEX:0X%X\r\n",res); //转为16进制
}else if(i!=4)return USMART_PARMERR;//参数错误.
else //参数显示设定功能
{
printf("16进制参数显示!\r\n");
usmart_dev.sptype=SP_TYPE_HEX;
}
}else return USMART_PARMERR; //参数错误.
printf("\r\n");
break;
case 5://dec指令
printf("\r\n");
usmart_get_aparm(str,sfname,&i);
if(i==0)//参数正常
{
i=usmart_str2num(sfname,&res); //记录该参数
if(i==0) //进制转换功能
{
printf("DEC:%lu\r\n",res); //转为10进制
}else if(i!=4)return USMART_PARMERR;//参数错误.
else //参数显示设定功能
{
printf("10进制参数显示!\r\n");
usmart_dev.sptype=SP_TYPE_DEC;
}
}else return USMART_PARMERR; //参数错误.
printf("\r\n");
break;
case 6://runtime指令,设置是否显示函数执行时间
printf("\r\n");
usmart_get_aparm(str,sfname,&i);
if(i==0)//参数正常
{
i=usmart_str2num(sfname,&res); //记录该参数
if(i==0) //读取指定地址数据功能
{
if(USMART_ENTIMX_SCAN==0)printf("\r\nError! \r\nTo EN RunTime function,Please set USMART_ENTIMX_SCAN = 1 first!\r\n");//报错
else
{
usmart_dev.runtimeflag=res;
if(usmart_dev.runtimeflag)printf("Run Time Calculation ON\r\n");
else printf("Run Time Calculation OFF\r\n");
}
}else return USMART_PARMERR; //未带参数,或者参数错误
}else return USMART_PARMERR; //参数错误.
printf("\r\n");
break;
default://非法指令
return USMART_FUNCERR;
}
return 0;
}
//移植注意:本例是以stm32为例,如果要移植到其他mcu,请做相应修改.
//usmart_reset_runtime,清除函数运行时间,连同定时器的计数寄存器以及标志位一起清零.并设置重装载值为最大,以最大限度的延长计时时间.
//usmart_get_runtime,获取函数运行时间,通过读取CNT值获取,由于usmart是通过中断调用的函数,所以定时器中断不再有效,此时最大限度
//只能统计2次CNT的值,也就是清零后+溢出一次,当溢出超过2次,没法处理,所以最大延时,控制在:2*计数器CNT*0.1ms.对STM32来说,是:13.1s左右
//其他的:TIM4_IRQHandler和Timer4_Init,需要根据MCU特点自行修改.确保计数器计数频率为:10Khz即可.另外,定时器不要开启自动重装载功能!!
#if USMART_ENTIMX_SCAN==1
//复位runtime
//需要根据所移植到的MCU的定时器参数进行修改
void usmart_reset_runtime(void)
{
__HAL_TIM_CLEAR_FLAG(&TIM4_Handler,TIM_FLAG_UPDATE);//清除中断标志位
__HAL_TIM_SET_AUTORELOAD(&TIM4_Handler,0XFFFF); //将重装载值设置到最大
__HAL_TIM_SET_COUNTER(&TIM4_Handler,0); //清空定时器的CNT
usmart_dev.runtime=0;
}
//获得runtime时间
//返回值:执行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms
//需要根据所移植到的MCU的定时器参数进行修改
uint32_t usmart_get_runtime(void)
{
if(__HAL_TIM_GET_FLAG(&TIM4_Handler,TIM_FLAG_UPDATE)==SET)//在运行期间,产生了定时器溢出
{
usmart_dev.runtime+=0XFFFF;
}
usmart_dev.runtime+=__HAL_TIM_GET_COUNTER(&TIM4_Handler);
return usmart_dev.runtime; //返回计数值
}
//下面这两个函数,非USMART函数,放到这里,仅仅方便移植.
//定时器4中断服务程序
void TIM4_IRQHandler(void)
{
if(__HAL_TIM_GET_IT_SOURCE(&TIM4_Handler,TIM_IT_UPDATE)==SET)//溢出中断
{
usmart_dev.scan(); //执行usmart扫描
__HAL_TIM_SET_COUNTER(&TIM4_Handler,0);; //清空定时器的CNT
__HAL_TIM_SET_AUTORELOAD(&TIM4_Handler,100);//恢复原来的设置
}
__HAL_TIM_CLEAR_IT(&TIM4_Handler, TIM_IT_UPDATE);//清除中断标志位
}
//使能定时器4,使能中断.
void Timer4_Init(uint16_t arr,uint16_t psc)
{
//定时器4
__HAL_RCC_TIM4_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM4_IRQn,3,3); //设置中断优先级,抢占优先级3,子优先级3
HAL_NVIC_EnableIRQ(TIM4_IRQn); //开启ITM4中断
TIM4_Handler.Instance=TIM4; //通用定时器4
TIM4_Handler.Init.Prescaler=psc; //分频
TIM4_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM4_Handler.Init.Period=arr; //自动装载值
TIM4_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&TIM4_Handler);
HAL_TIM_Base_Start_IT(&TIM4_Handler); //使能定时器4和定时器4中断
}
#endif
//初始化串口控制器
//sysclk:系统时钟(Mhz)
void usmart_init(uint8_t sysclk)
{
#if USMART_ENTIMX_SCAN==1
Timer4_Init(1000,(uint32_t)sysclk*100-1);//分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步.
#endif
usmart_dev.sptype=1; //十六进制显示参数
}
//从str中获取函数名,id,及参数信息
//*str:字符串指针.
//返回值:0,识别成功;其他,错误代码.
uint8_t usmart_cmd_rec(uint8_t*str)
{
uint8_t sta,i,rval;//状态
uint8_t rpnum,spnum;
uint8_t rfname[MAX_FNAME_LEN];//暂存空间,用于存放接收到的函数名
uint8_t sfname[MAX_FNAME_LEN];//存放本地函数名
sta=usmart_get_fname(str,rfname,&rpnum,&rval);//得到接收到的数据的函数名及参数个数
if(sta)return sta;//错误
for(i=0;i<usmart_dev.fnum;i++)
{
sta=usmart_get_fname((uint8_t*)usmart_dev.funs[i].name,sfname,&spnum,&rval);//得到本地函数名及参数个数
if(sta)return sta;//本地解析有误
if(usmart_strcmp(sfname,rfname)==0)//相等
{
if(spnum>rpnum)return USMART_PARMERR;//参数错误(输入参数比源函数参数少)
usmart_dev.id=i;//记录函数ID.
break;//跳出.
}
}
if(i==usmart_dev.fnum)return USMART_NOFUNCFIND; //未找到匹配的函数
sta=usmart_get_fparam(str,&i); //得到函数参数个数
if(sta)return sta; //返回错误
usmart_dev.pnum=i; //参数个数记录
return USMART_OK;
}
//usamrt执行函数
//该函数用于最终执行从串口收到的有效函数.
//最多支持10个参数的函数,更多的参数支持也很容易实现.不过用的很少.一般5个左右的参数的函数已经很少见了.
//该函数会在串口打印执行情况.以:"函数名(参数1,参数2...参数N)=返回值".的形式打印.
//当所执行的函数没有返回值的时候,所打印的返回值是一个无意义的数据.
void usmart_exe(void)
{
uint8_t id,i;
uint32_t res;
uint32_t temp[MAX_PARM];//参数转换,使之支持了字符串
uint8_t sfname[MAX_FNAME_LEN];//存放本地函数名
uint8_t pnum,rval;
id=usmart_dev.id;
if(id>=usmart_dev.fnum)return;//不执行.
usmart_get_fname((uint8_t*)usmart_dev.funs[id].name,sfname,&pnum,&rval);//得到本地函数名,及参数个数
printf("\r\n%s(",sfname);//输出正要执行的函数名
for(i=0;i<pnum;i++)//输出参数
{
if(usmart_dev.parmtype&(1<<i))//参数是字符串
{
printf("%c",'"');
printf("%s",usmart_dev.parm+usmart_get_parmpos(i));
printf("%c",'"');
temp[i]=(uint32_t)&(usmart_dev.parm[usmart_get_parmpos(i)]);
}else //参数是数字
{
temp[i]=*(uint32_t*)(usmart_dev.parm+usmart_get_parmpos(i));
if(usmart_dev.sptype==SP_TYPE_DEC)printf("%ld",temp[i]);//10进制参数显示
else printf("0X%X",temp[i]);//16进制参数显示
}
if(i!=pnum-1)printf(",");
}
printf(")");
#if USMART_ENTIMX_SCAN==1
usmart_reset_runtime(); //计时器清零,开始计时
#endif
switch(usmart_dev.pnum)
{
case 0://无参数(void类型)
res=(*(uint32_t(*)())usmart_dev.funs[id].func)();
break;
case 1://有1个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0]);
break;
case 2://有2个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1]);
break;
case 3://有3个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2]);
break;
case 4://有4个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3]);
break;
case 5://有5个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4]);
break;
case 6://有6个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
temp[5]);
break;
case 7://有7个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
temp[5],temp[6]);
break;
case 8://有8个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
temp[5],temp[6],temp[7]);
break;
case 9://有9个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
temp[5],temp[6],temp[7],temp[8]);
break;
case 10://有10个参数
res=(*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\
temp[5],temp[6],temp[7],temp[8],temp[9]);
break;
}
#if USMART_ENTIMX_SCAN==1
usmart_get_runtime();//获取函数执行时间
#endif
if(rval==1)//需要返回值.
{
if(usmart_dev.sptype==SP_TYPE_DEC)printf("=%lu;\r\n",res);//输出执行结果(10进制参数显示)
else printf("=0X%X;\r\n",res);//输出执行结果(16进制参数显示)
}else printf(";\r\n"); //不需要返回值,直接输出结束
if(usmart_dev.runtimeflag) //需要显示函数执行时间
{
printf("Function Run Time:%d.%1dms\r\n",usmart_dev.runtime/10,usmart_dev.runtime%10);//打印函数执行时间
}
}
//usmart扫描函数
//通过调用该函数,实现usmart的各个控制.该函数需要每隔一定时间被调用一次
//以及时执行从串口发过来的各个函数.
//本函数可以在中断里面调用,从而实现自动管理.
//如果非ALIENTEK用户,则USART_RX_STA和USART_RX_BUF[]需要用户自己实现
void usmart_scan(void)
{
uint8_t sta,len;
if(USART_RX_STA&0x8000)//串口接收完成?
{
len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度
USART_RX_BUF[len]='\0'; //在末尾加入结束符.
sta=usmart_dev.cmd_rec(USART_RX_BUF);//得到函数各个信息
if(sta==0)usmart_dev.exe(); //执行函数
else
{
len=usmart_sys_cmd_exe(USART_RX_BUF);
if(len!=USMART_FUNCERR)sta=len;
if(sta)
{
switch(sta)
{
case USMART_FUNCERR:
printf("函数错误!\r\n");
break;
case USMART_PARMERR:
printf("参数错误!\r\n");
break;
case USMART_PARMOVER:
printf("参数太多!\r\n");
break;
case USMART_NOFUNCFIND:
printf("未找到匹配的函数!\r\n");
break;
}
}
}
USART_RX_STA=0;//状态寄存器清空
}
}
#if USMART_USE_WRFUNS==1 //如果使能了读写操作
//读取指定地址的值
uint32_t read_addr(uint32_t addr)
{
return *(uint32_t*)addr;//
}
//在指定地址写入指定的值
void write_addr(uint32_t addr,uint32_t val)
{
*(uint32_t*)addr=val;
}
#endif
总结:
1、通过串口发送led_set()来控制LED等亮灭
2、
3、
4、
5、
6、如果要用到工程中uart的程序需要改进,当串口出错后就无法再进入接收中断了
7、在cubemx中没有添加串口、定时器,需要在"stm32f7xx_hal_conf.h"文件中
#define HAL_TIM_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
并且在HAL_DRIVER中添加stm32f7xx_hal_uart.c