Harman 세미콘 아카데미/SoC를 위한 Peripheral 설계

2024.9.11 [SoC를 위한 Peripheral 설계]4 - stopwatch_btn(myip), pwm_servo(myip)

U_Pong 2024. 9. 11. 23:16

2024.9.11 수업날


< stopwatch_btn(myip) > 

저번에 만들었던 stopwatch_btn Block Desgin에서 이번에는 버튼에 기능을 추가해본다.

 

vivado 진행 순서

board 부분에서 4 Puch Button을 끌어다가 Diagram에 추가한다.

 

Diagram 부분에서 수정한 부분이 있다면 Sources에서 수정한 Diagram의 wrapper를 갱신한다.

그 다음 bitstream을 실행하고 .xsa 파일을 만든다.

 

 

vitis 진행 순서

새롭게 만든 hardware platform에는 gpio(버튼 기능)이 있는 것을 확인할 수 있다.

 

Mblaze_stopwatch_btn -> helloworld.c 소스코드
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xgpio.h"

#define BTN_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define BTN_CHANNEL 1

#define STOPWATCH_BASEADDR XPAR_MYIP_STOPWATCH_0_S00_AXI_BASEADDR

int main()
{
    init_platform();

    print("Start!\n");
    XGpio_Config *cfg_ptr;
    XGpio btn_device;

    u32 btn_value;
    u32 lap_toggle = 1;
    u32 start_toggle = 0b10;

    cfg_ptr = XGpio_LookupConfig(BTN_ID);
    XGpio_CfgInitialize(&btn_device, cfg_ptr, cfg_ptr->BaseAddress);
    XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, 0xffff);

    volatile unsigned int *stopwatch_instance = (volatile unsigned int)STOPWATCH_BASEADDR;
    xil_printf("Sanity check : %x \n", stopwatch_instance[7]);	// 무결성 체크

    while(1){
    	if(XGpio_DiscreteRead(&btn_device, BTN_CHANNEL)){		// 채터링 방지
    		MB_Sleep(1);
    		btn_value = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL);

			// lap: 0b01
			if(btn_value == 0b0001){
				stopwatch_instance[0] = stopwatch_instance[0] ^ lap_toggle;	// bit toggle
				lap_toggle = 0;
			}
			else if(btn_value == 0b0000){
				lap_toggle = 1;
			}

			// start: 0b10, stop: 0b00
			if(btn_value == 0b0010){
				stopwatch_instance[0] = stopwatch_instance[0] ^ start_toggle;	// bit toggle
				start_toggle = 0;
			}
			else if(btn_value == 0b0000){
				start_toggle = 0b10;
			}
    	}


//    	if(btn_value == 0b0001 && (stopwatch_instance[0] & 0b01))
//    		stopwatch_instance[0] = stopwatch_instance[0] & 0b10;	// bit clear
//    	else if(btn_value == 0b0001 && ~(stopwatch_instance[0] & 0b01))
//    		stopwatch_instance[0] = stopwatch_instance[0] | 1;		// bit set
//
//    	// start: 0b10, stop: 0b00
//    	if(btn_value == 0b0001 && (stopwatch_instance[0]&0b10))stopwatch_instance[0] = 0b00;
//    	else if(btn_value == 0b0001 && ~(stopwatch_instance[0]&0b10))stopwatch_instance[0] = 0b10;
//
//    	// lap: 0b01
//    	else if(btn_value == 0b0010 && (stopwatch_instance[0]&0b1))stopwatch_instance[0] = 0b01;
//    	else if(btn_value == 0b0010 && ~(stopwatch_instance[0]&0b1))stopwatch_instance[0] = 0b00;

    	//stopwatch_instance[0] = 0b10;
//		xil_printf("button %x\n", btn_value);
//		xil_printf("control reg %x\n", stopwatch_instance[0]);
//    	print("Hello World!\n");
//    	MB_Sleep(1000);
    }

    cleanup_platform();
    return 0;
}

 

stopwatch_btn_app 결과 영상

 

 

 

< pwm_servo(myip) > 

이번에는 pwm을 사용하여 servo motor를 제어하는 IP를 만들어본다.

Verilog 수업시간에 만들었던 pwm 모듈을 가지고 IP로 만든다.

pwm_256step_comparator_freq
//////////////////////////pwm 제어하기, 256분주기(100MHz로 나눈), 비교기 사용
// 서브모터는 50Hz
module pwm_256step_comparator_freq 
#(  parameter sys_clk_freq = 100_000_000,           //시스템 클록,  100MHz
      parameter pwm_freq = 50,                                   //pwm 출력, 50Hz
      parameter duty_step = 256,                                // 듀티 싸이클 1256로 설정
      parameter temp = sys_clk_freq / pwm_freq / duty_step,         //  타이머 카운트 주기, parameter 상수는 그냥 수이기 때문에 회로 만들때는 나누기 회로가 만들어지지 않음
      parameter temp_half = temp / 2)
(
      input clk, reset_p,
      input [31:0]duty,             
      output reg pwm );     
      
      integer cnt;                        // 카운터 변수 선언
      reg pwm_freqX256;          // 128배 주파수를 가지는 PWM 신호를 저장하는 레지스터 선언
      always @ (posedge clk or posedge reset_p) begin
            if(reset_p) begin
                  pwm_freqX256 = 0;
                  cnt = 0;
            end
            else begin
                  if(cnt >= (temp - 1))cnt = 0;               //78분주-->10,000/128
                  else cnt = cnt + 1;
                  
                  //if(cnt < (pwmXduty_steps / 2)) pwm_freqX128 = 0;                  // 마찬가지로 상수이기 때문에 상관 없음
                  if(cnt < temp_half) pwm_freqX256 = 0;
                  else pwm_freqX256 = 1;
            end
      end
      
      wire pwm_freqX256_nedge;
       edge_detector_n ed(
                  .clk(clk), .reset_p(reset_p), 
                  .cp(pwm_freqX256), .n_edge(pwm_freqX256_nedge));
      
      integer cnt_duty;  
      always @ (posedge clk or posedge reset_p)begin
            if(reset_p) begin
                  cnt_duty = 0;
                  pwm = 0;
            end
            else if(pwm_freqX256_nedge)begin
                  if(cnt_duty >= (duty_step - 1))cnt_duty= 0;
                  else cnt_duty = cnt_duty + 1;
                  
                  if(cnt_duty < duty)pwm = 1;
                  else pwm = 0;
            end
      end
      
endmodule

 

 

vivado 진행 순서

Block Desgin에서 새로 생성하고 기본적인 Block Diagram까지 만든 후 IP를 만들면 된다.

 

 

아래와 같이 파일 추가

 

 

slave 모듈 수정해야할 부분

 

 

top 모듈 수정해야할 부분

 

 

package IP에서 초록 체크표시가 되지 않은 부분에서 노란 부분을 선택하면 초록 체크표시로 바뀐다.

마지막으로 re-package IP를 선택한다.

 

 

myip_pwm 추가된 모습

출력단에 출력을 추가해준다.

 

 

Block Design에서 Sources의 .xdc에 아래와 같이 수정한다.

 

wrapper를 갱신하고 bistream을 실행한 후 .xsa를 생성하면 된다.

 

 

vitis 진행 순서

 

Mblaze_pwm_servo -> helloworld.c 소스코드
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"

#define SERVO_BASEADDR XPAR_MYIP_PWM_0_S00_AXI_BASEADDR


int main()
{
	char up_down = 1;
    init_platform();

    print("Start!\n");

    volatile unsigned int *servo_instance = (volatile unsigned int)SERVO_BASEADDR;
    xil_printf("Sanity check : %x \n", servo_instance[7]);
    servo_instance[0] = 6;

    while(1){
    	MB_Sleep(16);
    	if(up_down){
    		servo_instance[0]++;
    		if(servo_instance[0] >= 32) up_down = 0;
    	}
    	else{
    		servo_instance[0]--;
			if(servo_instance[0] <= 6) up_down = 1;
		}

//    	print("Hello World!\n");
//    	MB_Sleep(1000);
    }

    cleanup_platform();
    return 0;
}

 

pwm _servo _app 결과

stopwatch_btn(myip), pwm_servo(myip) 끝!