woshi_ziyu
发表于: 2015-10-16 16:09:37 | 显示全部楼层

如何使用STM32 NUCLEO的串口


how-to-use-stm32-nucleo-serial-port-660x330.jpg


正如我们在前面有关ST新的开发板的教程看到的,STM32 NUCLEO板集成了ST-LINK V2.1接口。ST-LINK的设计主要是通过MINI-USB接口对目标MCU进行编程。而且,它至少提供了另一种非常有用的功能:虚拟串口。当你安装完ST-LINK的驱动后,在硬件设备列表中就会出现新的设备:ST-LINK虚拟串口。

342.jpg


如果使用的是Linux的PC或Mac,你会在/ dev目录中发现一个新的终端设备。一般情况下,该设备会以类似于tty.usbmodemXXXX被命名,如下所示:

Schermata-2015-01-24-alle-08.40.43.jpg

串口在大多数情况下是有用的,这有两个原因:如果你想要调试你的固件的打印信息(对于ARM架构不是绝对必要的,因为我们也可以使用ARM半主控),或者如果你想要在NUCLEO板和PC之间交换指令和消息(或许,你正在使用创建中的专用的应用程序)。

在这篇文章中我将演示如何正确地配置和使用STM32 NUCLEO板集成的虚拟串口。但是,我们开始编写代码之前,先来看看硬件,这可能是真正有用的。 ST提供STM32 NUCLEO板的全部硬件项目(该板的设计使用Altium Designer的CAD,电子工业中使用的专业的CAD工具,但你不需要有一个如此昂贵的软件来使用您的NUCLEO板)。我采用的是NUCLEO - F401RE型号,但它应该是很容易重新安排指令以便正确使用你的具体的NUCLEO板。


一、引脚

像STM32复杂而灵活的MCU都提供复用功能的的I / O。这意味着,在我们可以用一个外围设备(如我们的例子中的USART)之前,需要配置和相应引脚的相关外设。查看STM32CubeMX工具,我们发现,STM32F401RETx处理器有3个不同的USART:USART1,USART2和USART6。

2015-01-24-08_52_46-STM32CubeMX-uart2.ioc_-STM32F401RETx-1024x782.png


现在我们来看一看NUCLEO板的原理图。正如我们在下图看到的,USART_TX和USART_RX端口连接到PA2和PA3引脚。也就是说,NUCLEO被配置为使用目标MCU的USART2外设。

stm32-nucleo-usart-pinout-1024x460.jpg

到此,我们已经手握需要开始编程所需的硬件相关的所有必要信息。


二、代码

在我们开始配置USART2外设时,我们需要建立一个测试工程。我们将使用GCC ARM Eclipse插件生成一个空项目,正如我在系列关于STM32平台的GCC工具链中的介绍。当您生成测试工程时,可以使用下面的配置参数。

Schermata-2015-01-24-alle-09.17.09.png

如果你已经按照我以前的GNU Eclipse插件教程,那么你已经知道如何使用插件生成NUCLEO-F4板不正确的时钟配置。我已经展示了如何使用STM32CubeMX工具生成正确的时钟初始化代码。为简单起见,这是你必须把以下代码放入到_initialize_hardware.c文件中。

  1. void
  2. configure_system_clock(void)
  3. {
  4.         RCC_OscInitTypeDef RCC_OscInitStruct;
  5.         RCC_ClkInitTypeDef RCC_ClkInitStruct;

  6.         __PWR_CLK_ENABLE();

  7.         __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  8.         RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  9.         RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  10.         RCC_OscInitStruct.HSICalibrationValue = 6;
  11.         RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  12.         RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  13.         RCC_OscInitStruct.PLL.PLLM = 16;
  14.         RCC_OscInitStruct.PLL.PLLN = 336;
  15.         RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  16.         RCC_OscInitStruct.PLL.PLLQ = 7;
  17.         HAL_RCC_OscConfig(&RCC_OscInitStruct);

  18.         RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
  19.         RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  20.         RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  21.         RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  22.         RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  23.         HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
  24. }
复制代码

下一步,我们要添加一个配置USART接口的函数。我们定义为MX_USART2_UART_Init,如下所示。

  1. UART_HandleTypeDef huart2;
  2. ...
  3. void MX_USART2_UART_Init(void)
  4. {
  5.         huart2.Instance = USART2;
  6.         huart2.Init.BaudRate = 115200;
  7.         huart2.Init.WordLength = UART_WORDLENGTH_8B;
  8.         huart2.Init.StopBits = UART_STOPBITS_1;
  9.         huart2.Init.Parity = UART_PARITY_NONE;
  10.         huart2.Init.Mode = UART_MODE_TX_RX;
  11.         huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  12.         HAL_UART_Init(&huart2);
  13. }
复制代码

该函数非常容易理解,huart2是UART_HandleTypeDef描述符的一个实例。它是用于配置UART外设的结构体。但是,此代码仍不足以使用UART。我们仍需要配置硬件部分,配置正确的引脚和初始化相关UART外设正确的时钟。这项工作由下面的钩子函数完成:

  1. ...
  2. void HAL_UART_MspInit(UART_HandleTypeDef* huart)
  3. {
  4.   GPIO_InitTypeDef GPIO_InitStruct;
  5.   if(huart->Instance==USART2)
  6.   {
  7.     __GPIOA_CLK_ENABLE();
  8.     __USART2_CLK_ENABLE();

  9.     /**USART2 GPIO Configuration
  10.     PA2     ------> USART2_TX
  11.     PA3     ------> USART2_RX
  12.     */
  13.     GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
  14.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  15.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  16.     GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  17.     GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
  18.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  19.   }
  20. }
  21. ...
复制代码

即使在这个例子里,该代码也是很容易理解的。首先,我们需要初始化PORTA GPIO的外设时钟。接下来,我们需要启用与UART2外设相关的时钟。最后,我们必须正确配置PIN2、PIN3作为UART2 TX和RX UART2。

在这值得一提的一个重要方面是,我们并不需要在初始化部分显式调用该功能。这是一个钩子函数,由我们在函数MX_USART2_UART_Init()内调用的HAL_UART_Init()函数自动调用。调用这个初始化函数的正确地方是在_initialize_hardware.c文件的__initialize_hardware()函数中。

那么现在,我们只需要编写一个简单的main()函数来测试UART。

  1. int main(int argc, char* argv[])
  2. {
  3.   char *msg = "Hello Nucleo Fun!\n\r";

  4.   HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 0xFFFF);
  5.   while(1);
  6. }
复制代码

main()函数非常简单:它只是在UART打印输出一条消息和一直挂起。

我们编译整个项目之前,我们需要做另一项最后的操作。默认情况下,Eclipse的GNU-ARM插件禁用未使用的STM32 HAL文件,以加快编译操作。因此,我们需要启用stm32f4xx_hal_uart.c文件的编译。

进入到Project Explorer->system->src->stm32f4-hal ,然后在stm32f4xx_hal_uart.c文件上点击鼠标右键,如下图所示:

Schermata-02-2457080-alle-13.25.27-1024x640.png

点击“属性”,然后转到C / C ++编译和取消选中“从生成排除”,如下图所示。

Schermata-02-2457080-alle-14.10.44.png

现在,我们可以编译测试工程,并使用GDB和OpenOCD上传到我们的NUCLEO板。


要想看到在UART的消息,根据使用的操作系统你有几个选项。在Eclipse市场,你会发现一些终端仿真器插件。 TCF是其中之一。对于Windows操作系统另一个选择是使用putty。在Mac和Linux ckermit是一个合适的选择。

Schermata-02-2457080-alle-15.08.19-1024x161.png

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 28 | 回复: 50



手机版|

GMT+8, 2024-4-28 14:17 , Processed in 0.077687 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表