串口在嵌入式领域是一个非常重要的功能,它不仅用来做普通的通信,还可以作为一种通用的调试手段。 在STM32F4系列芯片中,大多都同时支持4-8个串口,这一章我们参考Keil版本串口通信代码,实现一个Rust版本的串口。
其核心是配置IO端口复用,以及串口时钟及中断。 其中USART1_IRQHandler函数是固定的用来处理USART1的中断功能函数,在startup_stm32f40_41xxx.s文件中引用。
#include "stm32f4xx.h" #include "stm32f4xx_usart.h" #define UART1_LEN 256 u8 uart1_buff[UART1_LEN]; int uart1_idx = 0; char uart1_send_flag = 0; void Delay(__IO uint32_t nTime); void en_my_uart1(void); int main(void) { SystemInit(); //IRQ group set NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); /* Configure MCO1 pin(PA8) in alternate function */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOF, &GPIO_InitStructure); en_my_uart1(); while (1) { GPIO_SetBits(GPIOF, GPIO_Pin_9|GPIO_Pin_10); Delay(0x7FFFFF); GPIO_ResetBits(GPIOF, GPIO_Pin_9|GPIO_Pin_10); Delay(0x7FFFFF); if( uart1_send_flag ){ for(int i=0;i<uart1_idx;i++){ USART_SendData(USART1, uart1_buff[i]); } uart1_idx = 0; uart1_send_flag = 0; } } } void USART1_IRQHandler(void){ if( uart1_send_flag ){ return; } if( USART_GetITStatus(USART1, USART_IT_RXNE) ){ u8 d = 0x00FF & USART_ReceiveData(USART1); uart1_buff[uart1_idx] = d; uart1_idx += 1; if( d == 0x0D){ uart1_send_flag |= 0x01; // command over return; } } } void Delay(__IO uint32_t nTime){ while(nTime--); } void en_my_uart1(){ // enable clock RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // set Pin AF GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // init Pin GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // UART init USART_InitTypeDef uart_init_cfg; uart_init_cfg.USART_BaudRate = 115200; uart_init_cfg.USART_HardwareFlowControl = USART_HardwareFlowControl_None; uart_init_cfg.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; uart_init_cfg.USART_Parity = USART_Parity_No; uart_init_cfg.USART_StopBits = USART_StopBits_1; uart_init_cfg.USART_WordLength = USART_WordLength_8b; USART_Init(USART1 , &uart_init_cfg); // UART IRQ config USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //IRQ Config NVIC_InitTypeDef nvic_init_cfg; nvic_init_cfg.NVIC_IRQChannel = USART1_IRQn; nvic_init_cfg.NVIC_IRQChannelCmd = ENABLE; nvic_init_cfg.NVIC_IRQChannelPreemptionPriority = 0x01; nvic_init_cfg.NVIC_IRQChannelSubPriority = 0x01; NVIC_Init(&nvic_init_cfg); // enable USART_Cmd(USART1, ENABLE); }为了方便,我们仍然使用HAL版本的依赖库,在LED实验代码基础上修改:
#![deny(unsafe_code)] #![no_main] #![no_std] // Halt on panic // #[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964 extern crate panic_halt; // panic handler use cortex_m; use cortex_m_rt::entry; use stm32f4xx_hal as hal; use nb::block; use hal::{prelude::*, stm32, serial}; #[entry] fn main() -> ! { if let (Some(dp), Some(cp)) = ( stm32::Peripherals::take(), cortex_m::peripheral::Peripherals::take(), ) { // Set up the system clock let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.sysclk(168.mhz()).freeze(); // Set up the LED. let gf = dp.GPIOF.split(); let mut led = gf.pf10.into_push_pull_output(); // Create a delay abstraction based on SysTick let mut delay = hal::delay::Delay::new(cp.SYST, clocks); // serial let ga = dp.GPIOA.split(); dp.USART1.cr1.modify(|_,w|{ w.rxneie().set_bit() }); let (mut tx, mut rx) = serial::Serial::usart1( dp.USART1, (ga.pa9.into_alternate_af7(), ga.pa10.into_alternate_af7() ), serial::config::Config::default().baudrate(115_200.bps()), clocks ).unwrap().split(); loop { // On for 1s, off for 1s. led.set_high().unwrap(); delay.delay_ms(500_u32); for a in b"hello,world!\r\n"{ block!(tx.write(*a)).ok(); } led.set_low().unwrap(); delay.delay_ms(500_u32); for a in b"nice to meet you~\r\n"{ block!(tx.write(*a)).ok(); } } } loop {} }和C版本不太一样的是,这里我们没有使用中断接收串口消息。
Idea中Rust插件提示Expand declarative macros建议为第三个,否则很多代码没有提示效果!
所有的NVIC中断函数表,放在stm32f407/mod.rs中,中断映射已经提前准备好了,我们只需要按照设定好的函数处理中断就行。 开启中断的方式是通过Listen函数,提供event就行。 其中,中断函数是通过extern "C"方式映射的:
#[cfg(feature = "rt")] extern "C" { fn WWDG(); fn PVD(); fn TAMP_STAMP(); fn RTC_WKUP(); fn RCC(); fn EXTI0(); fn EXTI1(); fn EXTI2(); fn EXTI3(); fn EXTI4(); fn DMA1_STREAM0(); .... }在使用C编写嵌入式时,我们可以使用大量的全局变量来记录好保存状态,非常简单。 但是Rust编写嵌入式,在处理变量和函数的作用域时,和普通的RUST applicaiton一样,需要考虑作用域的问题。 特别是编写中断处理函数时,需要提前将所需要的变量声明在全局static。
下次,我们看看有没有可能改进它。