반응형

STM32F103 series MCU의 hardware SPI interface를 사용하기 위해서 아래와 같이 GPIO 설정이 필요로 한다.

 

STM32F103RBT6 datasheet

#define GPIO_HW_SPI            GPIOA
#define GPIO_HW_SPI_CS                  GPIO_Pin_4    // NSS PIN
#define GPIO_HW_SPI_SCLK                GPIO_Pin_5    // Clock
#define GPIO_HW_SPI_MISO                GPIO_Pin_6    // Master Input to Slave Output
#define GPIO_HW_SPI_MOSI                GPIO_Pin_7    // Master Output to Slave Input

#define HW_SPI_READ_BIT			0x80
#define HW_SPI_WRITE_BIT		0x00

/* Select hardware SPI: Chip Select pin low  */
#define HW_SPI_CS_LOW()       GPIO_ResetBits(GPIO_HW_SPI, GPIO_HW_SPI_CS)
/* Deselect hardware SPI: Chip Select pin high */
#define HW_SPI_CS_HIGH()      GPIO_SetBits(GPIO_HW_SPI, GPIO_HW_SPI_CS)

void MXM1160_HW_SPI_GPIO_Init(void){

    SPI_InitTypeDef  SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Enable SPI1 and GPIO clocks */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    /* Configure SPI1 pins: SCK, MISO and MOSI */
    GPIO_InitStructure.GPIO_Pin   =  GPIO_HW_SPI_SCLK | GPIO_HW_SPI_MOSI | GPIO_HW_SPI_MISO;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIO_HW_SPI, &GPIO_InitStructure);

    /* Configure I/O for the sensor select */
    GPIO_InitStructure.GPIO_Pin   =  GPIO_HW_SPI_CS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIO_HW_SPI, &GPIO_InitStructure);

    /* Deselect the hardware SPI : Sensor Select high */
    HW_SPI_CS_HIGH();

    /* SPI1 configuration */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 1;
    SPI_Init(SPI1, &SPI_InitStructure);

    /* Enable SPI1  */
    SPI_Cmd(SPI1, ENABLE);    
}

 

위의 'SPI_InitStructure.SPI_CPOL"와 'SPI_InitStructure.SPI_CPHA'는 사용자 개발 환경과 SPI 통신을 사용하는 slave chip의 통신 모드의 Clock Plarity (CPOL), Clock Phase (CPHA)의 의해 결정되므로 개별적으로 확인하길 바란다.

 

그 다음에 SPI 통신은 양방향 통신이기 때문에 Read / Write command에 따라 dummy byte 등 적절한 array index 관리를 해주면 된다.

 

void  HW_SPI_Multi_TXRX(uint8_t Cmd, uint8_t addr, uint8_t count, uint8_t  *pData)
{
    uint8_t BufferSize = count;
    uint8_t SPI_Dummy_Tx = 0x00;
    uint8_t SPI_Dummy_Rx = 0x00;
	
    // Check the Command mode and Address
    SPI_Dummy_Tx = Cmd | addr;

    HW_SPI_CS_LOW();

    /* Wait for SPIy Tx buffer empty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    /* Send SPIy data */
    SPI_I2S_SendData(SPI1, SPI_Dummy_Tx);

    /* Wait for SPIy data reception */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    /* Read SPIy received data */
    SPI_Dummy_Rx = SPI_I2S_ReceiveData(SPI1);

    while(count>1){

        /* Wait for SPIy Tx buffer empty */
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
        /* Send SPIy data */
        SPI_I2S_SendData(SPI1, pData[BufferSize-count]);

        /* Wait for SPIy data reception */
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
        /* Read SPIy received data */
        if ( Cmd == HW_SPI_READ_BIT ){
            pData[BufferSize-count] = SPI_I2S_ReceiveData(SPI1);
        }
        else{
            SPI_Dummy_Rx = SPI_I2S_ReceiveData(SPI1);
        }			
        count--;
    }

    /* Wait for SPIy Tx buffer empty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    /* Send SPIy data */
    SPI_I2S_SendData(SPI1, pData[BufferSize-count]);

    /* Wait for SPIy data reception */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    /* Read SPIy received data */
    if ( Cmd == HW_SPI_READ_BIT ){
        pData[BufferSize-count] = SPI_I2S_ReceiveData(SPI1);
    }
    else{
        SPI_Dummy_Rx = SPI_I2S_ReceiveData(SPI1);
    }
		
    HW_SPI_CS_HIGH();
}

 

마지막으로 SPI 통신의 waveform grpah 관련 자료는 구글링을 하면 되므로, 검색 스킬을 발휘하길 바란다.

 

끝.

반응형
반응형

어느날 갑자기 마주하게 된 메인 디스플레이 화면에 나타난 "No Signal" 차량 문제가 발생 하였다.

 

 

해당 문제는 다음과 같은 자동차 점검 및 정비 내역서를 살펴보면 "컨트롤 유닛 프로그래밍" 문제라고 한다. 그래서 전체적으로 펌웨어 업그레이드를 진행하게 되었다.

 

센터에서 펌웨어 업그레이드를 진행하는데 소요된 시간은 대략 3시간 정도 소요되었다. 그렇기에 미리 노트북이나 책을 가져가면 길고긴 시간을 잘 활용할 수 있으니 참고하시길 바란다.

 

그리고 이번에 발생한 "No Signal" 문제가 또다시 발생되면, 지금처럼 사진 촬영이 아닌 차량 번호를 포함한 동영상 촬영을 하여 서비스 센터에 재방문하여 자동차 점검 및 정비를 받아야 하는데, 펌웨어 업데이트를 하고도 동일 증상이 발생되면 해당 하드웨어 유닛(Hardware Unit)를 무상 교체하여 준다고 한다.

 

 

그래도 무상 워런티 서비스 기간(10만km 및 5년)내에 서비스 센터 다녀오는 시간을 생각하면 해당 불량이 또다시 발생하지 않았으면 한다.

반응형
반응형

어느날 문득 블로그를 살펴보다가, 나의 차량 구매 이력에 대하여 첫번째는 대우자동차의 라세티 프리미어 1.8, 두번째는 기아자동차의 올뉴쏘렌토SUV 2.0, 그리고 세번째로 나의 드림카였던 애마에 대한 사진이나 스토리가 없다는 것을 알아차리게 되었다.

 

해당 차량을 2018년 11월에 인수 받아서, 벌써 2년 가까이 타고 있는데 이제서야 나의 드림카 관련 사진을 올리게 된다.

 

다음 사진은 차량 계약 후, 길고긴 기다림 끝에 나의 드림카를 만나던 날이었다. 나와 계약 하였던 딜러의 센스있는 준비와 정성에 감사 인사를 거듭 드렸다.

 

BMW 530i Luxury Plus Xdrive

 

그로 부터 시간이 흐른 후, 이제 대략 25,192km를 바라보게 되는데, 지금까지 잔고장으로는 Display No Signal 현상과 어느날 냉각수 보충 경고 메시지가 떠서 급하게 냉각수 보충한 것말고는 큰고장 없이 잘 타고 있다.

 

my BMW application capture

 

BMW 530i Luxury Plus  Xdrive에 대한 사용기를 굳이 남기자면, 다음과 같은 대표적인 특징이 있다.

 

  • 7세대 5시리즈 G30 body
  • 엔진형식 : 직렬 4기통 싱글 터보 가솔린
  • 배기량 : 1998cc
  • 최대 출력 : 252hp / 5200rpm
  • 최대 토크 : 35.7kg.m / 1450~4800rpm
  • 변속기 : ZF 스텝트로닉 스포츠 8단 A.T.
  • 복합연비 : 10.3 [도심 9.1 / 고속 12.3] km/L
  • 최고 안전속도 : 250km/h
  • 0 to 100 km/h : 6.0 sec
  • 전장/전폭/전고/휠베이스 : 4935 / 1870 / 1480 / 2975 mm
  • 공차중량 : 1790kg
  • 트렁크 용량 : 530L
  • 연료탱크 용량 : 68L
  • 타이어 규격 : [앞/뒤] 240 / 40 R19
  • 18인치 W스포크 휠 632
  • NAPPA 천연가죽 컴포트 시트
  • 전동 운전자 및 동승자 시트 메모리 기능
  • 40:20:40 분할 뒷좌석 폴딩
  • 2존 에어 컨디셔닝
  • 전자식 파킹 브레이크 [오토홀드]
  • 앰비언트 라이트 [색상 6가지 / 조합 11]

 

차량 계기판

 

  • 반자율 주행 지원 [ACC : Active Cruze Control] + 교통 정체 보조 기능
  • 차선유지보조 및 차선이탈경고 시스템
  • 액티브 측면충돌보호시스템, 사각지대 경고 시스템, 접근 알람, 충돌 회피 보조
  • iDriver 6.0 connection drive 적용
  • HIFI Loudspeaker 오디오 시스템
  • 제스쳐 컨트롤
  • 무선 충전 시스템
  • Head Up Display

 

그나저나 나의 평생 소원이였던 예서엄마의 화끈한 지원(?)에 고맙기도 하고, 소중한 보물인 예서와 함께 전국적으로 가족여행을 다니면서 Fun Driving Enjoy를 누리고 있다.

 

마지막으로 언제나 무사 안전운전을 기원하며!

반응형
반응형

어느샌가 1us 마다 정확하게 delay 시키는 함수 및 방법이 궁금해서 아래와 같이 자료를 정리한다.

 

결과적으로 STM32F103에서 제공하는 timer interrupt event 처리로 1ms 마다 거의 정확하게 delay 함수를 사용할 수 있지만, 1us 마다 delay 함수 사용하는 것은 사용자 MCU 및 컴파일러 환경에 따라 달라지는 것을 확인할 수가 있다. 그럼에도 불구하고 거의 1us에 가깝게 GPIO toggling할 수 있는 방법을 찾고자 한다면 괜찮지 않을까?

 

구글링에서 검색되는 delay_us 함수 중에 가장 많이 검색되는 function structure는 아래와 같다.

 

void Delay_us(__IO u32 nCount)
{
    for (; nCount != 0;nCount--);
}

void Delay_us(__IO uint32_t nCount)
{
	if(nCount>1){
			uint32_t count=nCount*8-6;
			while(count--); //asm volatile("nop");
	}
	else{
		uint32_t count=2;
		while(count--); // asm volatile("nop");
	}
}

 

실험을 해보니, 굳이 번거롭게 및 제법 있어보이게(?) 어셈블러 코드 같은거 갖다 쓰는데, 나와 같이 평범한 사람들은 그냥 아래와 같은 간단한 코드를 사용하면 된다.

 

#define NOP_1   asm volatile("NOP")
#define NOP_2  NOP_1; NOP_1
#define NOP_4  NOP_2; NOP_2
#define NOP_10   NOP_4; NOP_4; NOP_2
#define NOP_20  NOP_10; NOP_10
#define NOP_40  NOP_20; NOP_20

void delay_1us(bool state){ // High & LOW

	if (state)
	{
		NOP_20;
	}

	else{
		NOP_40;
	}
}

void GPIO_Toggle(){

	GPIO_ResetBits(GPIOC, GPIO_Pin_0);
	delay_1us(LOW);
	GPIO_SetBits(GPIOC, GPIO_Pin_0);
	delay_1us(HIGH);
}

 

이클립스 개발 환경에서 default option으로 컴파일을 하고, delay_us(1)마다 GPIO toggling을 하면 아래와 같은 파형이 관찰된다. 아울러 High / Low level timing도 서로 다른 것도 확인할 수가 있다.

 

그래서 정확하게 delay_us(1) 마다 GPIO toggling을 하는 방법은 아래와 같이, 컴파일 옵션 중에 Optimization Level을 "Optimize size (-Os)"를 선택하고 컴파일을 하면 다음과 같은 파형(High 및 LOW)을 얻을 수가 있다.

 

반응형
반응형

본인은 STM32F103 시리즈 MCU를 사용하고 있으며, Interrupt 기능을 사용하기 위해 GPIOA Pin 4번을 이용하여 인터럽트 관련 설정 코드를 아래와 같이 하였다.

 

void STM32_Interrupt_initial(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_AFIO, ENABLE);

	// GPIOA Pin 4 Interrupt
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	// Connect EXTI Line to gpio pin
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4);

	// Configure EXTI Line to generate an interrupt
	EXTI_InitStructure.EXTI_Line    = EXTI_Line4;
	EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);

	// Configure the NVIC Preemption Priority Bits
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

	// Enable the EXTI4 Interrupt
	NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

EXTI 및 NVIC 관련 설정은 구글링을 하면 많은 자료가 검색 되므로, 굳이 설명을 하지 않기로 한다.

참고로 "EXTI1_IRQHandler" ~ "EXTI4_IRQHandler", "EXTI9_5_IRQHandler", "EXTI15_10_IRQHandler"의 IRQ 핸들러를 설정할 수 있는데, Pin0 부터 Pin4까지는 지정된 IRQHandler를 사용해야 하고, Pin5 부터 Pin9는 EXTI9_5_IRQHandler를 사용해야 한다. 마찬가지로 Pin10 부터 Pin15는 EXTI15_10_IRQHandler를사용해야한다.

 

/*******************************************************************************
* Function Name  : EXTI4_IRQHandler
* Description    : This function handles External interrupt Line 4 request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void EXTI4_IRQHandler(void)
{
	if(EXTI_GetITStatus(GPIO_Pin_4) != RESET)
	{
		printf("Interrupt OK\n");

		// Clear the Interrupt EXTI line pending bit
		EXTI_ClearITPendingBit(GPIO_Pin_4);		
	}
}

이렇게 하여, 해당 GPIO Pin에 Failling Signal을 전달하면, 아래와 같이 debug message가 나타난다.

 

 

반응형

+ Recent posts