电量仪DIY手记——基本输入与输出系统(附Keil使用窍门)
在大多数的项目里,我们都需要有用户交互,或通过键盘,或通过网口,至少也会有LED灯的指示。今天我们就利用STM32L053套件的绿色指示灯、蓝色按键再加上串口显示来实现一个基本的用户交互。
考虑目前为实验,所以我们使用ST官方提供的代码生成软件STcubeMx来自动生成项目文件。我们需要配置下列几项:
1、为了方便配置我们首先选择Nucleo-STM32L053开发板,这样绿色的指示灯引脚,蓝色按键以及串口都为我们选定好了。
2、修改蓝色按键的引脚配置方式,原方案为边沿触发中断的方式,我们需要修改为普通输入的方式,因为我们要使用按键扫描的方式来获取按键状态。这里注意一下,按键在开发板上为硬件的上拉电阻,即按键引脚PC13默认值为高电平,在有按键按下时为低电平。
3、串口的方式我们使用DMA发送,接收使用中断的方式来实现。这里我们先行实现DMA方式。
4、我们再配置TIM21来实现按键扫描的中断处理,这里仅使用update中断,即溢出中断。我们设置为10ms触发一次中断。
配置完成我们就可以生成项目了,还是挺方便的。
我们在生成的项目源代码里仍然需要编写自己的代码,如启动中断以及串口DMA的地址配置等等。有兴趣大家可以参照一下源代码:
/** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* USER CODE BEGIN Includes */ #include "key.h" /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ extern KeyCodeClass gKeyCode; const char WelcomeStr[] = "hello EEPW!\r\n"; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ static void LL_Init(void); void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_USART2_UART_Init(void); static void MX_LPTIM1_Init(void); static void MX_TIM21_Init(void); /* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE END PFP */ /* USER CODE BEGIN 0 */ const uint16_t SLOT_25MS = 409; /* USER CODE END 0 */ /** * @brief The application entry point. * * @retval None */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ LL_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(); MX_DMA_Init(); MX_USART2_UART_Init(); MX_LPTIM1_Init(); MX_TIM21_Init(); /* USER CODE BEGIN 2 */ LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4, (uint32_t)WelcomeStr); LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_4, 0x40004428); LL_USART_EnableDMAReq_TX(USART2); /* config LPTIM1 */ LL_LPTIM_EnableIT_CMPM(LPTIM1); LL_LPTIM_Enable(LPTIM1); LL_LPTIM_SetAutoReload(LPTIM1, 0xFFFF); LL_LPTIM_SetCompare(LPTIM1, 20 * SLOT_25MS); LL_LPTIM_StartCounter(LPTIM1, LL_LPTIM_OPERATING_MODE_CONTINUOUS); /* LPTIM1 interrupt Init */ NVIC_SetPriority(TIM21_IRQn, 0); NVIC_EnableIRQ(TIM21_IRQn); LL_TIM_EnableIT_UPDATE(TIM21); LL_TIM_EnableCounter(TIM21); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if(gKeyCode.Click == 0x01) { gKeyCode.Click = 0; LL_mDelay(500); LL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, 13); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4); } } /* USER CODE END 3 */ } static void LL_Init(void) { LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); /* System interrupt init*/ /* SVC_IRQn interrupt configuration */ NVIC_SetPriority(SVC_IRQn, 0); /* PendSV_IRQn interrupt configuration */ NVIC_SetPriority(PendSV_IRQn, 0); /* SysTick_IRQn interrupt configuration */ NVIC_SetPriority(SysTick_IRQn, 0); } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) { Error_Handler(); } LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_RCC_MSI_Enable(); /* Wait till MSI is ready */ while(LL_RCC_MSI_IsReady() != 1) { } LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6); LL_RCC_MSI_SetCalibTrimming(0); LL_PWR_EnableBkUpAccess(); LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_LOW); LL_RCC_LSE_Enable(); /* Wait till LSE is ready */ while(LL_RCC_LSE_IsReady() != 1) { } LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI); /* Wait till System clock is ready */ while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI) { } LL_Init1msTick(4194000); LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK); LL_SetSystemCoreClock(4194000); LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_PCLK1); LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSE); /* SysTick_IRQn interrupt configuration */ NVIC_SetPriority(SysTick_IRQn, 0); } /* LPTIM1 init function */ static void MX_LPTIM1_Init(void) { /* Peripheral clock enable */ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPTIM1); /* LPTIM1 interrupt Init */ NVIC_SetPriority(LPTIM1_IRQn, 0); NVIC_EnableIRQ(LPTIM1_IRQn); LL_LPTIM_SetClockSource(LPTIM1, LL_LPTIM_CLK_SOURCE_INTERNAL); LL_LPTIM_SetPrescaler(LPTIM1, LL_LPTIM_PRESCALER_DIV2); LL_LPTIM_SetPolarity(LPTIM1, LL_LPTIM_OUTPUT_POLARITY_REGULAR); LL_LPTIM_SetUpdateMode(LPTIM1, LL_LPTIM_UPDATE_MODE_IMMEDIATE); LL_LPTIM_SetCounterMode(LPTIM1, LL_LPTIM_COUNTER_MODE_INTERNAL); LL_LPTIM_TrigSw(LPTIM1); } /* TIM21 init function */ static void MX_TIM21_Init(void) { LL_TIM_InitTypeDef TIM_InitStruct; /* Peripheral clock enable */ LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21); TIM_InitStruct.Prescaler = 2096; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 9; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV2; LL_TIM_Init(TIM21, &TIM_InitStruct); LL_TIM_SetClockSource(TIM21, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_SetTriggerOutput(TIM21, LL_TIM_TRGO_UPDATE); LL_TIM_DisableMasterSlaveMode(TIM21); } /* USART2 init function */ static void MX_USART2_UART_Init(void) { LL_USART_InitTypeDef USART_InitStruct; LL_GPIO_InitTypeDef GPIO_InitStruct; /* Peripheral clock enable */ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = USART_TX_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; GPIO_InitStruct.Alternate = LL_GPIO_AF_4; LL_GPIO_Init(USART_TX_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = USART_RX_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; GPIO_InitStruct.Alternate = LL_GPIO_AF_4; LL_GPIO_Init(USART_RX_GPIO_Port, &GPIO_InitStruct); /* USART2 DMA Init */ /* USART2_TX Init */ LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_4, LL_DMA_REQUEST_4); LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PRIORITY_LOW); LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_NORMAL); LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PERIPH_NOINCREMENT); LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MEMORY_INCREMENT); LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PDATAALIGN_BYTE); LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MDATAALIGN_BYTE); /* USART2 interrupt Init */ NVIC_SetPriority(USART2_IRQn, 0); NVIC_EnableIRQ(USART2_IRQn); USART_InitStruct.BaudRate = 9600; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; USART_InitStruct.StopBits = LL_USART_STOPBITS_1; USART_InitStruct.Parity = LL_USART_PARITY_NONE; USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; LL_USART_Init(USART2, &USART_InitStruct); LL_USART_DisableOverrunDetect(USART2); LL_USART_ConfigAsyncMode(USART2); LL_USART_Enable(USART2); } /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* Init with LL driver */ /* DMA controller clock enable */ LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); /* DMA interrupt init */ /* DMA1_Channel4_5_6_7_IRQn interrupt configuration */ NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0); NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn); } /** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */ static void MX_GPIO_Init(void) { LL_GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC); LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOH); LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); /**/ LL_GPIO_ResetOutputPin(LD2_GPIO_Port, LD2_Pin); /**/ GPIO_InitStruct.Pin = LL_GPIO_PIN_13; GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT; GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; LL_GPIO_Init(GPIOC, &GPIO_InitStruct); /**/ GPIO_InitStruct.Pin = LD2_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; LL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct); } /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
窍门:我们使用Keil打开项目后,首先编译一下(快捷键F7),这样的好处是Keil就能够识别我们关键字与函数了,可以方便关键词提示与函数跳转(光标在关键词或者函数上时按F12就快速跳转了)。