chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

STM32/RustでHALを使わずレジスタを直接操作してUART send/receive

ひきつづき、HALを使わず、素でレジスタを叩いてUART用のsend/receiveルーチンを作成
以下のソースはシステムクロック4HMzで、38400bpsで通信する。抽象化していないので、STM32L47xxxシリーズ専用。
変数型もu32でなくてもいいところがあるけど、、無駄が多くてもまずは動かすのが目標
UARTが使えるようになったので、次はI2Cでセンサと接続する。

//
//  stm32 device driver
// 

pub const GPIOA_BASE: u32 = 0x4800_0000;   //APB2PERIPH_BASE + 0x0800;
pub const MODER_OFFSET: u32 = 0x0;  // GPIOA
pub const BSRR_OFFSET: u32 = 0x18;
pub const AFRL_OFFSET: u32 = 0x20;


pub const RCC_BASE: u32 = 0x4002_1000;
pub const AHB2ENR_OFFSET : u32 = 0x4C;   // RCC_AHB2ENR
pub const APB1ENR1_OFFSET: u32 = 0x58;

pub const GPIO_PIN_2 : u32 = 2;
pub const GPIO_PIN_3 : u32 = 3;
pub const GPIO_PIN_5 : u32 = 5;

pub const RCC_CCIPR : u32 = 0x88;
pub const USART2_SEL : u32 = 1;
pub const USART2_BASE : u32 = 0x4000_4400;
pub const USART2EN : u32 = 17;
pub const BRR_OFFSET : u32 = 0x0c;
pub const ISR_OFFSET : u32 = 0x1c;
pub const CR1_OFFSET : u32 = 0;
pub const RDR_OFFSET: u32 = 0x24;
pub const TDR_OFFSET: u32 = 0x28;

pub const UE_BIT : u8 = 0;
pub const RE_BIT : u8 = 2;
pub const TE_BIT : u8 = 3;
pub const TC_BIT : u8 = 6;
pub const TXE_BIT : u8 = 7;
pub const RXNE_BIT : u8 = 5;


pub fn serial_putc(ch : u8){
   // send data
   let mut addr = (USART2_BASE + TDR_OFFSET) as *mut u32;  // 0x28
   let ch32 : u32 = ch as u32;
   unsafe {   
      core::ptr::write_volatile(addr, ch32);
      //let _ = my_wait(0x1);
   }

   // wait until TC(Transmission complete) is set
   let mut val : u32 = 0;
   addr = (USART2_BASE + ISR_OFFSET) as *mut u32;  // 0x1c
   while (val & (1 << TC_BIT)) == 0 {
      unsafe {   
         val = core::ptr::read_volatile(addr);
       }
   }
}

pub fn serial_getc()-> u8 {

   // wait until RXNE is set
   let mut val : u32 = 0;
   let mut addr = (USART2_BASE + ISR_OFFSET) as *mut u32;  // 0x1c
   while (val & (1 << RXNE_BIT)) == 0 {
      unsafe {   
         val = core::ptr::read_volatile(addr);
       }
   }

   // recv data
   addr = (USART2_BASE + RDR_OFFSET) as *mut u32;  // 0x28
   let val : u32;
   unsafe {   
      val = core::ptr::read_volatile(addr);
   }
   let ch = val as u8;
   ch 
}


pub fn setup_serial(){

  // set GPIO  PA2, PA3 to alternate mode
  let mut addr = (GPIOA_BASE + MODER_OFFSET) as *mut u32;
  let mut set_mode  = 0x2 << (GPIO_PIN_2 * 2); // 0x2 ... Alternate MODE
  set_mode |= 0x2 << (GPIO_PIN_3 * 2);         // 0x2 ... Alternate MODE
  unsafe { 
	let current = core::ptr::read_volatile(addr) ;
        let mut val = current & 0xFF_FF_FF_0F;
        val |= set_mode;
	core::ptr::write_volatile(addr, val) ;
  }

  // set GPIO  PA2, PA3 to AF7
  addr = (GPIOA_BASE + AFRL_OFFSET) as *mut u32;
  set_mode  = 0x7 << (GPIO_PIN_2 * 4); // 0x7 ... UART
  set_mode |= 0x7 << (GPIO_PIN_3 * 4); // 0x7 ... UART
  unsafe { 
	let current = core::ptr::read_volatile(addr) ;
        let mut val = current & 0xFF_FF_00_FF;
        val |= set_mode;
	core::ptr::write_volatile(addr, val) ;
  }

  // enable
  addr = (RCC_BASE + APB1ENR1_OFFSET) as *mut u32; // 0x58
  set_mode = 0x1 << USART2EN;        // USART2EN 17
  unsafe { 
	let current = core::ptr::read_volatile(addr) ;
        let mut val = current & 0xFF_FC_FF_FF;
        val |= set_mode;
         core::ptr::write_volatile(addr, val);
  }

  // set clock (USART2SEL)
  addr = (RCC_BASE + RCC_CCIPR) as *mut u32;    // 0x88
  set_mode = 0x1 << (USART2_SEL * 2 );  //01 ....  SYSCLK
  unsafe { 
	let current = core::ptr::read_volatile(addr) ;
        let mut val = current & 0xFF_FF_FF_F3;
        val |= set_mode;
	core::ptr::write_volatile(addr, val) ;
  }

  // set baud rate
  let div = 104;  // 38400  (4MHz/38400 = 104.166666)
  addr = (USART2_BASE + BRR_OFFSET) as *mut u32;  // 
  unsafe { 
	core::ptr::write_volatile(addr, div) ;
  }

  // enable UE/TE/RE
  set_mode = 0x1 << UE_BIT;            // UE_BIT = 0
  set_mode |= 0x1 << TE_BIT;           // TE_BIT = 3
  set_mode |= 0x1 << RE_BIT;           // RE_BIT = 2
  addr = (USART2_BASE + CR1_OFFSET) as *mut u32;  // 
  unsafe { 
	let current = core::ptr::read_volatile(addr) ;
        let mut val = current & 0xff_ff_ff_f2;
        val |= set_mode;
	core::ptr::write_volatile(addr, val) ;
  }

  // send data      just test
  addr = (USART2_BASE + TDR_OFFSET) as *mut u32;  // 0x28
  for _i in 0..20 {
      unsafe {   
           let val = 0x33;
   	   core::ptr::write_volatile(addr, val) ;
      }
      let _ = my_wait(0x10);
   }
}

参考にした動画。。。アドレスとかビット配列が違うが分かりやすい。感謝
#3. How to Configure UART using REGISTERS || STM32F4 - YouTube
I2C版は以下
#4. STM32F4 I2C Using Registers || Master mode || NO HAL || - YouTube