【实验记录】使用Keil 5开发APM32F10x(C8T6)核心板程序[0](LED,USART,串口DMA接收)

实验目的

项目背景

希望做一款舵机复现手臂运动的桌面装置,分为穿戴端和桌面端。

穿戴端计划采用单片机收集2个陀螺仪数据,通过无线收发装置(验证版采用zigbee透传模块,后续考虑接入局域网控制)。

本实验目的为开发穿戴端的收发程序。

开发环境选择

网购了两块开发板,商家商品标注不明,实际使用的是国产的pin-to-pin兼容芯片。但这也导致难以使用Cube IDE进行开发(烧写会报错“ Could not verify ST device! Abort connection.”有教程进行跳过:ST-Link设备连接。 Could not verify ST device! Abort connection._error in final launch sequence: failed to start gd-CSDN博客)。不过笔者电脑未安装STM32 ST-LINK Utility,遂作罢。选择Keil 5开发。

实验过程

SDK安装

芯片是Geehy的,公司官网:珠海极海半导体有限公司 (geehy.com)

SDK下载地址:珠海极海半导体有限公司 | APM32F103系列 (geehy.com)(向下拉,选择APM32F10x_SDK 与 APM32F1xx_DFP Pack 下载,前者是代码库,后者是Keil Pack开发板资源)

芯片数据手册:APM32F103xC数据手册 V1.7.pdf (geehy.com)

我使用的不是官方推荐的APM32F103VC MINI开发板(使用手册:APM32F103VC MINI开发板使用说明书V1.1.pdf (geehy.com)),而是下面这款商品:https://m.tb.cn/h.gjxTn2heTm3fWv3?tk=d4Uo32ljgY6

实拍照片:

缺少部分外设,不过不影响使用官网软件开发包进行开发。

环境配置

先双击安装开发板资源。

为了节省时间,我选择直接基于部分例程(APM32F10x_SDK_V1.8\Examples\GPIO\GPIO_Toggle)进行开发。后续有时间自己再重走一遍。

程序烧写采用ST-LINK连接(菜单栏:Flash -> Configure Flash Tools... -> Debug -> 下图标注处,选择连接方式)。

代码编写

通过Keil 5打开项目文件夹APM32F10x_SDK_V1.8\Examples\GPIO\GPIO_Toggle\Project\MDK\GPIO_Toggle.uvprojx,进入项目编辑。

点灯(GPIO输出)

修改 Board_APM32F103_MINI.h 中的部分宏定义以适配开发板。开发板载LED(除电源指示LED)引脚PC13。找到相关代码,修改引脚(代码为修改后):

/** @addtogroup APM32F103_MINI_LED
  * @{
  */
#define LEDn                             2

#define LED2_PIN                         GPIO_PIN_13
#define LED2_GPIO_PORT                   GPIOC
#define LED2_GPIO_CLK                    RCM_APB2_PERIPH_GPIOC

然后就可以在main函数(所有#include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"的文件)中调用以下函数:

void APM_MINI_LEDInit(Led_TypeDef Led);
void APM_MINI_LEDOn(Led_TypeDef Led);
void APM_MINI_LEDOff(Led_TypeDef Led);
void APM_MINI_LEDToggle(Led_TypeDef Led);

操作LED2了。同样地,也可以将LED3重定义为某些与LED操作方式一致的外设。

串口回响

在打开DMA前,先实现串口回响(收什么发什么)。

新增文件uart_driver.h、uart_driver.c,撰写串口相关函数。注意新增的头文件要放入指定的文件夹(如本项目中的APM32F10x_SDK_V1.8\Examples\GPIO\GPIO_Toggle\Include),如果想自定义头文件需要自行添加目录(菜单栏:Flash -> Configure Flash Tools... -> C/C++ -> 下图标注处,添加路径)。

 在uart_driver.c中定义USART1的初始化函数、中断服务函数与收发函数:

// 文件:uart_driver.c
// 功能:串口回响

#include <stdint.h>

#include "apm32f10x_usart.h"
// #include "apm32f10x_dma.h"    //未开启DMA时不需要
#include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"

#include "uart_driver.h"

// USART1 TEST 初始化
// 非DMA版本
void USART1_Init()
{
	
	// 使用结构体初始化串口USART1
	USART_Config_T U1_Configer;
	
	U1_Configer.baudRate = 9600;	// 波特率,9600
	U1_Configer.wordLength = USART_WORD_LEN_8B;	// 数据位,8bit
	U1_Configer.stopBits = USART_STOP_BIT_1;	// 停止位,1bit
	U1_Configer.parity = USART_PARITY_NONE;	//校验位,0bit
	U1_Configer.mode = USART_MODE_TX_RX;	// 模式,收发
	U1_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE;	// 硬件流控制,无
	
	APM_MINI_COMInit(COM1, &U1_Configer);    // 这是SDK自带的函数,包含GPIO初始化及串口设置
	__NVIC_EnableIRQ(USART1_IRQn);    // 与STM32不同的函数
	USART_EnableInterrupt(USART1, USART_INT_RXBNE);    // 开启串口1的接收缓冲区非空中断(RXBNE,与STM的RBNE不同)
	
}
//

// USART1接收中断处理函数
void USART1_IRQHandler(void)
{
	if (USART_ReadIntFlag(USART1, USART_INT_RXBNE) != RESET)
	{
        // 读取接收到的数据
        uint16_t data[2] = {USART_RxData(USART1), 0};

        // 发送数据回显
        USART1_Send_Str(data);
		
        // 清除接收中断标志位
        USART_ClearIntFlag(USART1, USART_INT_RXBNE);
		
	    // 重新开启中断
	    USART_EnableInterrupt(USART1, USART_INT_RXBNE);
	}

}
//

// 串口发送字符串函数
void USART1_Send_Str(uint8_t* str)
{
	uint8_t cnt = 0;
	while (*str && cnt <= USART1_TX_MAX_LEN) {
				cnt ++;
				USART_TxData(USART1, *str++);
        while (USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET) {
            // 等待发送完成
        }
    }
}

在uart_driver.h文件中声明相关函数:

// 文件:uart_driver.h
// 功能:串口回响

#ifndef _UART_DRIVER_H_
#define _UART_DRIVER_H_

#include "apm32f10x_usart.h"

void USART1_Init(void);
void USART1_IRQHandler(void);
void USART1_Send_Str(uint8_t* str);

#endif

main.c中#include "uart_driver.h",main函数:

int main(void)
{
	// ==================== 外设初始化 ====================
    APM_MINI_LEDInit(LED2);
		APM_MINI_LEDOff(LED2);
		USART1_Init();
		USART1_DMA_Init();
	
    while (1)
    {
	        // ==================== 发送测试 ====================

			USART_TxData(USART1, 'X');
			Delay();
			
    }

测试效果:

串口DMA接收(常规发送)

实现与JY62的交互。该模块可通过串口以定频率(可调节,≤256Hz)持续发送数据帧,并可通过串口发送指令进行设置。详见:JY62产品资料 (yuque.com)

常规发送功能即可满足需求,接收功能有两种解决方案:(1)DMA接收定长数据:开启传输完成中断(DMA_INT_TC),每接收2个数据帧长度触发中断服务,进行数据帧截取与读取;(2)DMA接收不定长数据:开启串口空闲中断,每个数据帧之间中断触发中断服务,进行数据帧截取与读取。

这里先选择实现第一个方案,第二个后续找时间实现。根据资料,USART1 RX 对应的DMA通道为 DMA1 Channel5。在uart_driver.c中定义相关的初始化函数、中断服务函数与收发函数:

// 文件:uart_driver.c
// 功能:串口1 DMA定长消息接收

#include <stdint.h>
#include <stdio.h>

// ================================================== Std Periph Driver ==================================================
#include "apm32f10x_usart.h"
#include "apm32f10x_dma.h"
#include "apm32f10x_misc.h"
#include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"

#include "uart_driver.h"
#include "pwm_driver.h"
#include "jy62.h"    // 我自己写的JY62消息解码程序头文件
                     // MPU_frame_data数组就是上面这个文件定义的extern变量
#include "main.h"

uint8_t USART1_RX_Buffer[64] = {0};
#define USART1_RX_BUFFER_SIZE 64
#define USART1_TX_MAX_LEN 32

// USART1 DMA接收 初始化
void USART1_DMA_Init(void)
{
	// 开启相关外设时钟
  RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOA);
  RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
  RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
	
	// GPIO初始化
	GPIO_Config_T GPIO_configStruct;
  /* Configure USART Tx as alternate function push-pull */
  GPIO_configStruct.mode = GPIO_MODE_AF_PP;
  GPIO_configStruct.pin = GPIO_PIN_9;
  GPIO_configStruct.speed = GPIO_SPEED_50MHz;
  GPIO_Config(GPIOA, &GPIO_configStruct);
  /* Configure USART Rx as input floating */
  GPIO_configStruct.mode = GPIO_MODE_IN_FLOATING;
  GPIO_configStruct.pin = GPIO_PIN_10;
  GPIO_Config(GPIOB, &GPIO_configStruct);

	// 初始化串口USART1
	USART_Config_T U1_Configer;
	
	U1_Configer.baudRate = 9600;	// 波特率,9600
	U1_Configer.wordLength = USART_WORD_LEN_8B;	// 数据位,8bit
	U1_Configer.stopBits = USART_STOP_BIT_1;	// 停止位,1bit
	U1_Configer.parity = USART_PARITY_NONE;	//校验位,0bit
	U1_Configer.mode = USART_MODE_TX_RX;	// 模式,收发
	U1_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE;	// 硬件流控制,无
  /* USART configuration */
  USART_Config(USART1, &U1_Configer);
  /* Enable USART */
	USART_EnableRx(USART1);
	USART_EnableTx(USART1);
  USART_Enable(USART1);
	
	
	// 配置 DMA
	DMA_Config_T DMA_ConfigStructure;
		
  DMA_ConfigStructure.peripheralBaseAddr = (uint32_t)/*(USART1->DATA)*/ (&(USART1->DATA_B)); // 外设地址
  DMA_ConfigStructure.memoryBaseAddr = (uint32_t)USART1_RX_Buffer; 	// 内存地址
  DMA_ConfigStructure.dir = DMA_DIR_PERIPHERAL_SRC; 			// 外设作为数据源
  DMA_ConfigStructure.bufferSize = USART1_RX_BUFFER_SIZE; // 缓冲区大小
  DMA_ConfigStructure.peripheralInc = DMA_PERIPHERAL_INC_DISABLE; // 外设地址不增
  DMA_ConfigStructure.memoryInc = DMA_MEMORY_INC_ENABLE; 					// 内存地址增
  DMA_ConfigStructure.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE; // 外设数据宽度为8位
  DMA_ConfigStructure.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE; 				// 内存数据宽度为8位
  DMA_ConfigStructure.loopMode = DMA_MODE_NORMAL; 	// 正常模式
  DMA_ConfigStructure.priority = DMA_PRIORITY_HIGH; // 高优先级
  DMA_ConfigStructure.M2M = DMA_M2MEN_DISABLE; 			// 禁用内存到内存传输

  DMA_Reset(DMA1_Channel5);
  DMA_Config(DMA1_Channel5, &DMA_ConfigStructure); // 初始化DMA1通道 
  DMA_ConfigDataNumber(DMA1_Channel5, 22);    // 设置传输数据长度
	
  // 配置 DMA 中断
  NVIC_EnableIRQRequest(DMA1_Channel5_IRQn, 0, 0);    // 注册中断服务
  DMA_EnableInterrupt(DMA1_Channel5, DMA_INT_TC);
  // APM32的TC(transmit complete)中断相当于STM32的FTF(full transmit finish)中断
  // 这里注意不要把标志位 DMA1_FLAG_INT_TC5 和中断类型 DMA_INT_TC 搞混
	
	// 初始化后初次使能USART1 DMA接收
  USART_EnableDMA(USART1, USART_DMA_RX);
  DMA_Enable(DMA1_Channel5);
  USART1_DMA_Receive();
}
//


// 重启USART1 DMA接收函数
void USART1_DMA_Receive() 
{	
	
  // 使能 DMA 通道
  DMA_ConfigDataNumber(DMA1_Channel5, 22);
  DMA_Enable(DMA1_Channel5);
	
  // 配置USART1的DMA接收
  USART_EnableDMA(USART1, USART_DMA_RX);
	
}
//


// DMA接收中断处理函数
void DMA1_Channel5_IRQHandler(void)
{
	// 解码
	if(DMA_ReadIntFlag(DMA1_INT_FLAG_TC5))
	{
		// 清除中断标志
		DMA_ClearIntFlag(DMA1_INT_FLAG_TC5);
		
		// 去使能 DMA
		DMA_Disable(DMA1_Channel5);
		
		// 数据帧处理,从两数据帧长的接收信息中截取0x55开头的消息
        // 保证接收到,但牺牲了一半的数据频率
		uint8_t i = 0;
		while(i + 10 < 22 && USART1_RX_Buffer[i] != 0x55)
		{ i++;}
		uint8_t ii = 0;
		for(ii = 0; ii<10;ii++)
		{
			MPU_frame_data[ii] = USART1_RX_Buffer[i + ii];
		}
		MPU_frame_dataRecord();	// from JY62.h
		
		// 重使能 DMA
		USART1_DMA_Receive();
	}
}
//


// 串口1发送字符串函数
void USART1_Send_Str(uint8_t* str)
{
	uint8_t cnt = 0;
	while (*str && cnt <= USART1_TX_MAX_LEN) {
				cnt ++;
				USART_TxData(USART1, *str++);
        while (USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET) {
            // 等待发送完成
        }
    }
}
//

类似地在在uart_driver.h文件中声明相关函数。在main函数中加入数据回传、在中断服务函数中加入LED点亮函数,测试是否正常工作符合预期:

// 文件:main.c
// 函数:int main(void)
// 功能:隔时发送距中断剩余传输字节数

int main(void)
{
	// ================================================== 外设初始化 ==================================================
    APM_MINI_LEDInit(LED2);
	APM_MINI_LEDOff(LED2);
	USART1_DMA_Init();

    while (1)
    {
			// ================================================== 收发测试 ==================================================
			
			// 测试DMA传输数据cnt剩余量,正常应随字节接收从22到1后重置为22
			sprintf(txt, " %d \n", DMA_ReadDataNumber(DMA1_Channel5));
			USART1_Send_Str(txt);
			Delay();
			
    }
}

定时发送字节,测试效果如下,符合预期:

测试:隔时回传JY62姿态角

先仿照USART1的常规收发代码,初始化USART2作为数据回传串口。

// 文件:uart_driver.c
// 功能:串口1 DMA定长消息接收;串口2 常规收发,数据回传

#include <stdint.h>
#include <stdio.h>

// ================================================== Std Periph Driver ==================================================
#include "apm32f10x_usart.h"
#include "apm32f10x_dma.h"
#include "apm32f10x_misc.h"
#include "Board_APM32F103_MINI\inc\Board_APM32F103_MINI.h"

#include "uart_driver.h"
#include "pwm_driver.h"
#include "jy62.h"
#include "main.h"

uint8_t USART1_RX_Buffer[64] = {0};
#define USART1_RX_BUFFER_SIZE 64
#define USART1_TX_MAX_LEN 32

#define USART2_TX_MAX_LEN 32

// USART1 DMA接收 初始化
void USART1_DMA_Init(void)
{
	// 开启相关外设时钟
  RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOA);
  RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
	RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
	
	// GPIO初始化
	GPIO_Config_T GPIO_configStruct;
  /* Configure USART Tx as alternate function push-pull */
  GPIO_configStruct.mode = GPIO_MODE_AF_PP;
  GPIO_configStruct.pin = GPIO_PIN_9;
  GPIO_configStruct.speed = GPIO_SPEED_50MHz;
  GPIO_Config(GPIOA, &GPIO_configStruct);
  /* Configure USART Rx as input floating */
  GPIO_configStruct.mode = GPIO_MODE_IN_FLOATING;
  GPIO_configStruct.pin = GPIO_PIN_10;
  GPIO_Config(GPIOB, &GPIO_configStruct);

	// 初始化串口USART1
	USART_Config_T U1_Configer;
	
	U1_Configer.baudRate = 9600;	// 波特率,9600
	U1_Configer.wordLength = USART_WORD_LEN_8B;	// 数据位,8bit
	U1_Configer.stopBits = USART_STOP_BIT_1;	// 停止位,1bit
	U1_Configer.parity = USART_PARITY_NONE;	//校验位,0bit
	U1_Configer.mode = USART_MODE_TX_RX;	// 模式,收发
	U1_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE;	// 硬件流控制,无
  /* USART configuration */
  USART_Config(USART1, &U1_Configer);
  /* Enable USART */
	USART_EnableRx(USART1);
	USART_EnableTx(USART1);
  USART_Enable(USART1);
	
	
	// 配置 DMA
	DMA_Config_T DMA_ConfigStructure;
		
  DMA_ConfigStructure.peripheralBaseAddr = (uint32_t)/*(USART1->DATA)*/ (&(USART1->DATA_B)); // 外设地址
  DMA_ConfigStructure.memoryBaseAddr = (uint32_t)USART1_RX_Buffer; 	// 内存地址
  DMA_ConfigStructure.dir = DMA_DIR_PERIPHERAL_SRC; 			// 外设作为数据源
  DMA_ConfigStructure.bufferSize = USART1_RX_BUFFER_SIZE; // 缓冲区大小
  DMA_ConfigStructure.peripheralInc = DMA_PERIPHERAL_INC_DISABLE; // 外设地址不增
  DMA_ConfigStructure.memoryInc = DMA_MEMORY_INC_ENABLE; 					// 内存地址增
  DMA_ConfigStructure.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE; // 外设数据宽度为8位
  DMA_ConfigStructure.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE; 				// 内存数据宽度为8位
  DMA_ConfigStructure.loopMode = DMA_MODE_NORMAL; 	// 正常模式
  DMA_ConfigStructure.priority = DMA_PRIORITY_HIGH; // 高优先级
  DMA_ConfigStructure.M2M = DMA_M2MEN_DISABLE; 			// 禁用内存到内存传输

	DMA_Reset(DMA1_Channel5);
  DMA_Config(DMA1_Channel5, &DMA_ConfigStructure); // 初始化DMA1通道5
	DMA_ConfigDataNumber(DMA1_Channel5, 22);
	
  // 配置 DMA 中断
  NVIC_EnableIRQRequest(DMA1_Channel5_IRQn, 0, 0);
	DMA_EnableInterrupt(DMA1_Channel5, DMA_INT_TC);
	
	// 使能USART1 DMA接收
  USART_EnableDMA(USART1, USART_DMA_RX);
  DMA_Enable(DMA1_Channel5);
	USART1_DMA_Receive();
}
//


// 重启USART1 DMA接收函数
void USART1_DMA_Receive() 
{	
	
	// 使能 DMA 通道
	DMA_ConfigDataNumber(DMA1_Channel5, 22);
  DMA_Enable(DMA1_Channel5);
	
  // 配置USART1的DMA接收
  USART_EnableDMA(USART1, USART_DMA_RX);
	
}
//


// DMA接收中断处理函数
void DMA1_Channel5_IRQHandler(void)
{
	//debug
	APM_MINI_LEDOn(LED2);
	
	// 解码
	if(DMA_ReadIntFlag(DMA1_INT_FLAG_TC5))
	{
		// 清除中断标志
		DMA_ClearIntFlag(DMA1_INT_FLAG_TC5);
		
		// 去使能 DMA
		DMA_Disable(DMA1_Channel5);
		
		// 数据帧处理
		uint8_t i = 0;
		while(i + 10 < 22 && USART1_RX_Buffer[i] != 0x55)
		{ i++;}
		uint8_t ii = 0;
		for(ii = 0; ii<11; ii++)
		{
			MPU_frame_data[ii] = USART1_RX_Buffer[i + ii];
		}
		MPU_frame_dataRecord();	
		
		// 重使能 DMA
		USART1_DMA_Receive();
	}
}
//


// 串口1发送字符串函数
void USART1_Send_Str(uint8_t* str)
{
	uint8_t cnt = 0;
	while (*str && cnt <= USART1_TX_MAX_LEN) {
				cnt ++;
				USART_TxData(USART1, *str++);
        while (USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET) {
            // 等待发送完成
        }
    }
}
//


// USART2 初始化
void USART2_Init()
{
	
	// 使用结构体初始化串口USART2
	USART_Config_T U2_Configer;
	//NVIC_InitTypeDef NVIC_InitStructure;
	
	U2_Configer.baudRate = 9600;	// 波特率,9600
	U2_Configer.wordLength = USART_WORD_LEN_8B;	// 数据位,8bit
	U2_Configer.stopBits = USART_STOP_BIT_1;	// 停止位,1bit
	U2_Configer.parity = USART_PARITY_NONE;	//校验位,0bit
	U2_Configer.mode = USART_MODE_TX_RX;	// 模式,收发
	U2_Configer.hardwareFlow = USART_HARDWARE_FLOW_NONE;	// 硬件流控制,无
	
	APM_MINI_COMInit(COM2, &U2_Configer);
	NVIC_EnableIRQRequest(USART2_IRQn, 0, 0);
	USART_EnableInterrupt(USART2, USART_INT_RXBNE);
	
}

//
// USART1接收中断服务函数
void USART2_IRQHandler(void)
{
	if (USART_ReadIntFlag(USART2, USART_INT_RXBNE) != RESET)
	{
        // 读取接收到的数据
        uint16_t data = USART_RxData(USART2);

        // 这里添加服务

        // 清除接收中断标志位
        USART_ClearIntFlag(USART2, USART_INT_RXBNE);
		
		// 重新开启中断
		USART_EnableInterrupt(USART2, USART_INT_RXBNE);
	}
}
//

// 串口2发送字符串函数
void USART2_Send_Str(uint8_t* str)
{
	uint8_t cnt = 0;
	while (*str && cnt <= USART2_TX_MAX_LEN) {
				cnt ++;
				USART_TxData(USART2, *str++);
        while (USART_ReadStatusFlag(USART2, USART_FLAG_TXC) == RESET) {
            // 等待发送完成
        }
    }
}
//

main函数添加相关代码:

// 文件:main.c
// 函数:int main(void)
// 功能:隔时回传JY62姿态角

int main(void)
{
	// ================================================== 外设初始化 ==================================================
        APM_MINI_LEDInit(LED2);
		APM_MINI_LEDOff(LED2);

		USART2_Init();
		USART1_DMA_Init();

    while (1)
    {
			// ================================================== 数据回传测试 ==================================================		
			// 结构体来自jy62.h
			sprintf(txt, "r %f ", Angle.roll);
			USART2_Send_Str(txt);
			Delay();
			sprintf(txt, "p %f ", Angle.pitch);
			USART2_Send_Str(txt);
			Delay();
			sprintf(txt, "y %f ", Angle.yaw);
			USART2_Send_Str(txt);
			Delay();
			
    }
}

实验效果:

实验总结

  • 熟悉了APM32F10x的开发环境配置与库文件定义
  • 复现了常规外设(GPIO,USART,DMA)操作
  • 完成了部分模块的驱动移植,为下一步开发做准备

下次实验计划进行定时器TMR与PWM输出的驱动,以实现呼吸灯与舵机控制。

其他感想

第一次写博客,也是在对以往实验记录方式进行了反思之后做出的决定。以后会更多地把这些个人的小实验作线上记录,给自己一个动力,也希望能吸引到同好交流与指教。

APM32作为国产 Arm Cortex-M3 架构单片机的优秀代表之一,单片机性能不错,价格也比较实惠,对于个人开发者来说性价比良好。美中不足的是SDK提供的库文件与STM32的表述区别还是很多的,移植并没有那么容易;且部分注释仍然不够充足、清晰。不过充足的示例还是为学习提供了便利。以后会多考虑。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常感谢您的提问。以下是使用stm32f103c8t6keil5编写超声波代码的示例: ```c #include "stm32f10x.h" #define TRIG_PIN GPIO_Pin_0 #define ECHO_PIN GPIO_Pin_1 #define TRIG_PORT GPIOA #define ECHO_PORT GPIOA void delay_us(uint32_t us) { uint32_t i; for (i = 0; i < us * 8; i++) { __NOP(); } } void send_trigger_pulse(void) { GPIO_SetBits(TRIG_PORT, TRIG_PIN); delay_us(10); GPIO_ResetBits(TRIG_PORT, TRIG_PIN); } uint32_t measure_pulse_width(void) { uint32_t pulse_width = 0; while (GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == RESET); while (GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == SET) { pulse_width++; delay_us(1); } return pulse_width; } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = TRIG_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(TRIG_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = ECHO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(ECHO_PORT, &GPIO_InitStructure); while (1) { send_trigger_pulse(); uint32_t pulse_width = measure_pulse_width(); uint32_t distance = pulse_width / 58; // do something with distance } } ``` 这个代码使用PA0作为超声波发射引脚,PA1作为超声波接收引脚。当需要测量距离时,先发送一个10微秒的高电平脉冲到发射引脚,然后等待接收引脚上的脉冲,计算脉冲宽度并将其转换为距离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值