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

2024.9.12 [SoC를 위한 Peripheral 설계]5 - btn_intc(myip), iic(myip)

U_Pong 2024. 9. 12. 23:54

2024.9.12 수업날


< btn_intc(myip) >

이번에는 버튼 인터럽트를 하는 IP를 만들어본다.

Block Diagram에서 microblaze를 더블클릭하여 endable exception을 체크하여 인터럽트로 처리하도록 한다.

 

 

microblaze를 automation으로 할 때 아래와 같이 인터럽트 부분에 체크한다.

 

 

그러면 Diagram에서 인터럽트 컨트롤러가 추가된다.

 

 

axi_gpio에서 더블클릭하여 Enable Interrupt를 체크한다.

 

 

microblaze_0_xlconcat에서 number of ports를 2로 설정한다.

 

 

다음으로 Diagram에서 axi_gpio의 하나 남은 출력단과 microblaze_0_xlconcat를 수동으로 연결해준다.

 

 

다음으로 Diagram에서 axi_uartlite의 하나 남은 출력단과 microblaze_0_xlconcat를 수동으로 연결해준다.

 

 

그리고 좌측의 Flow navigator에서 Generate Block Design을 누른 후 global을 선택하여 generate를 실행한다.

 

 

다음으로 wrapper 갱신하고, bitsteam을 실행한 후에 .xsa를 생성한다.

 

 

vitis 진행 순서

 

 

인터럽트를 활성화 시키면 플렛폼에 인터럽드 컨트롤러가 추가된다.

 

새로운 .xsa를 생성하였으니 이를 기반으로 한 platform을 만들고, 기존에 만들었던 btn_inc_app에서

mblaze_btn_intc_app.prj를 선택한 후 우측의 platform에서 점 3개를 클릭한다.

설정을 바꾼 후에 캡처한 것이라 올바르게 바뀐 것이다

 

그리고 새롭게 만든 platform을 선택해주면 platform이 바뀐다.

굳이 app을 새롭게 만들 필요가 없다.

 

Mblaze_btn_intc -> helloworld.c 소스코드
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xgpio.h"
#include "xintc.h"
#include "xil_exception.h"
#include "xuartlite.h"

#define UART_ID XPAR_UARTLITE_0_DEVICE_ID
#define UART_VEC_ID XPAR_INTC_0_UARTLITE_0_VEC_ID

#define BTN_ID XPAR_GPIO_0_DEVICE_ID
#define BTN_CHANNEL 1

#define INCT_ID XPAR_MICROBLAZE_0_AXI_INTC_DEVICE_ID
#define BTN_VEC_ID XPAR_INTC_0_GPIO_0_VEC_ID

void BTN_ISR(void *CallBackRef);
void SendHandler(void *CallBackRef, unsigned int EventData);
void RecvHandler(void *CallBackRef, unsigned int EventData);

XGpio btn_instance;
XIntc intc_instance;
XUartLite uart_instance;
u32 btn_old_value;

int main()
{
    init_platform();

    print("Start!\n");

    XUartLite_Initialize(&uart_instance, UART_ID);

    XGpio_Config *cfg_ptr;
    cfg_ptr = XGpio_LookupConfig(BTN_ID);
    XGpio_CfgInitialize(&btn_instance, cfg_ptr, cfg_ptr->BaseAddress);
    XGpio_SetDataDirection(&btn_instance, BTN_CHANNEL, 0b1111);

    XIntc_Initialize(&intc_instance, INCT_ID);
    XIntc_Connect(&intc_instance, BTN_VEC_ID, (XInterruptHandler)BTN_ISR, (void *)&btn_instance);
    XIntc_Connect(&intc_instance, UART_VEC_ID,
    		(XInterruptHandler)XUartLite_InterruptHandler, (void *)&uart_instance);

    XIntc_Enable(&intc_instance, BTN_VEC_ID);
    XIntc_Enable(&intc_instance, UART_VEC_ID);
    XIntc_Start(&intc_instance, XIN_REAL_MODE);

    XGpio_InterruptEnable(&btn_instance, BTN_CHANNEL);
    XGpio_InterruptGlobalEnable(&btn_instance);

    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    		(Xil_ExceptionHandler)XIntc_InterruptHandler, &intc_instance);
    Xil_ExceptionEnable();

    XUartLite_SetRecvHandler(&uart_instance, RecvHandler, &uart_instance);
    XUartLite_SetSendHandler(&uart_instance, SendHandler, &uart_instance);
    XUartLite_EnableInterrupt(&uart_instance);

    while(1){
//    	xil_printf("btn_value : %d\n\r", XGpio_DiscreteRead(&btn_instance, BTN_CHANNEL));
//    	print("Hello World!\n");
//    	MB_Sleep(1000);
    }

    cleanup_platform();
    return 0;
}


void BTN_ISR(void *CallBackRef){
	XGpio *Gpio_ptr = (XGpio *)CallBackRef;

	if(XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL)){
		if(XGpio_DiscreteRead(Gpio_ptr,  BTN_CHANNEL) == 0b0001) print("btn 0 pushed!\n\r");
		else if(XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL) == 0b0010) print("btn 1 pushed!\n\r");
		else if(XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL) == 0b0100) print("btn 2 pushed!\n\r");
		else if(XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL) == 0b1000) print("btn 3 pushed!\n\r");
	}
	else {
		if(btn_old_value == 0b0001) print("btn 0 released!\n\r");
		else if(btn_old_value == 0b0010) print("btn 1 released!\n\r");
		else if(btn_old_value == 0b0100) print("btn 2 released!\n\r");
		else if(btn_old_value == 0b1000) print("btn 3 released!\n\r");
	}

//	if(!XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL)){				//btn0을 뗐을 때 인터럽트 활성화
//		if(btn_old_value == 0b0001)
//			print("GPIO interrupt\n\r");
//	}
	btn_old_value = XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL);
//	if(XGpio_DiscreteRead(Gpio_ptr, BTN_CHANNEL) & 0b0001){		//btn0을 눌렀을 때만 인터럽트 활성화
//		print("Gpio interrupt\n\r");
//	}
	//XGpio_InterruptClear(&btn_instance, BTN_CHANNEL);
	XGpio_InterruptClear(Gpio_ptr, BTN_CHANNEL);
	return;
}

void SendHandler(void *CallBackRef, unsigned int EventData){

	return;
}

void RecvHandler(void *CallBackRef, unsigned int EventData){
	u8 rxData;
	XUartLite_Recv(&uart_instance, &rxData, 1);
	xil_printf("Recv %c\n\r", rxData);
	return;
}

 

Mblaze_btn_intc 결과 영상

 

 

 

< iic(mychip) >

이번에는 I2C 통신을 사용하여 LCD에 글자를 띄우는 것을 해본다.

 

vivado 진행 순서

새로운 block design 생성

 

 

Diagram에서 iic를 검색해서 추가

 

 

Block Design에서 Sources의 micro_balze_iic_wrapper를 wrapper 하면 코드가 생기는데,

아래의 상자 부분을 .xdc에 추가해야한다.

 

 

Block Design의 .xdc 수정해야하는 부분

 

 

bitstream 실행하고 .xsa 생성

 

 

vitis 진행 순서

 

 

Mblaze_iic -> helloworld.c 소스코드(lcd 문자 띄우기)
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xiic.h"

#define IIC_ID XPAR_IIC_0_DEVICE_ID
#define DHT11_BASEADDR XPAR_MYIP_DHT11_0_S00_AXI_BASEADDR

#define LCD_RS			0
#define LCD_RW			1
#define LCD_E			2
#define LCD_BACKLIGHT	3

#define LCD_DEV_ADDR	(0x27<<1)	// address 0x27 beginning from bit 1, bit 0 is R/W_bar

#define COMMAND_DISPLAY_CLEAR	0x01
#define COMMAND_DISPLAY_ON		0x0C
#define COMMAND_DISPLAY_OFF		0x08
#define COMMAND_ENTRY_MODE		0x06
#define COMMAND_4BIT_MODE		0x28

static u8 I2C_LCD_Data;

XIic iic_instance;

void LCD_Data_4bit (u8 data);
void LCD_EnablePin();
void LCD_WriteCommand(uint8_t commandData);
void LCD_WriteData(uint8_t charData);
void LCD_Init();
void LCD_BackLightOn();
void LCD_GotoXY(uint8_t row, uint8_t col);
void LCD_WriteString(char *string);


int main()
{
    init_platform();
    u8 data_t[4] = {0,};	//0 -> backlight off
    u8 tx_data = 0xff;
    u8 cnt = 0;

    print("Start!\n\r");

    volatile unsigned int *dht11_instance = (volatile unsigned int) DHT11_BASEADDR;

    XIic_Initialize(&iic_instance, IIC_ID);
//    XIic_Send(iic_instance.BaseAddress, 0x27, data_t, 1, XIIC_STOP);
    LCD_Init();
    LCD_WriteString("Humidity : ??");
    LCD_GotoXY(1, 0);
    LCD_WriteString("Temperture : ??");

    while(1){
    	// lcd
    	LCD_WriteData('a' + cnt++);
    	//XIic_Send(iic_instance.BaseAddress, 0x27, data_t, 1, XIIC_STOP);
    	print("Hello World!\n");
    	MB_Sleep(1000);
    	//XIic_Send(iic_instance.BaseAddress, 0x27, &tx_data, 1, XIIC_STOP);
    	MB_Sleep(1000);
    }

    cleanup_platform();
    return 0;
}


void LCD_Data_4bit (u8 data)
{
	I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | (data & 0xf0);		// put upper four bits
	LCD_EnablePin();
	I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | ((data & 0x0f)<<4);	// put lower four bits
	LCD_EnablePin();

}

void LCD_EnablePin()
{
	I2C_LCD_Data &= ~(1<<LCD_E);
	XIic_Send(iic_instance.BaseAddress, 0x27, &I2C_LCD_Data, 1, XIIC_STOP);
	I2C_LCD_Data |= (1<<LCD_E);
	XIic_Send(iic_instance.BaseAddress, 0x27, &I2C_LCD_Data, 1, XIIC_STOP);
	I2C_LCD_Data &= ~(1<<LCD_E);
	XIic_Send(iic_instance.BaseAddress, 0x27, &I2C_LCD_Data, 1, XIIC_STOP);
	MB_Sleep(2);
}

void LCD_WriteCommand(uint8_t commandData)
{
	I2C_LCD_Data &= ~(1<<LCD_RS);					// enter instruction code mode
	I2C_LCD_Data &= ~(1<<LCD_RW);					// enter write mode
	LCD_Data_4bit(commandData);						// output data
}


void LCD_WriteData(uint8_t charData)
{
	I2C_LCD_Data |= (1<<LCD_RS);						// enter data mode
	I2C_LCD_Data &= ~(1<<LCD_RW);						// enter write mode
	LCD_Data_4bit(charData);						// output data
}

void LCD_Init()
{
	// see HD44780 datasheet page 45 for following init commands
	MB_Sleep(20);
	LCD_WriteCommand(0x03);
	MB_Sleep(5);
	LCD_WriteCommand(0x03);
	MB_Sleep(1);
	LCD_WriteCommand(0x03);

	LCD_WriteCommand(0x02);
	LCD_WriteCommand(COMMAND_4BIT_MODE);
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_BackLightOn();
}

void LCD_BackLightOn()
{
	I2C_LCD_Data |= (1<<LCD_BACKLIGHT);
}

void LCD_GotoXY(uint8_t row, uint8_t col)
{
	col %= 16;										// column width is within 16
	row %= 2;										// row length is within 2
	uint8_t address = (0x40 * row) + col;			// see HD44780 datasheet page 12
	uint8_t command = 0x80 + address;
	LCD_WriteCommand(command);
}

void LCD_WriteString(char *string)
{
	for (uint8_t i=0; string[i]; i++)
	{
		LCD_WriteData(string[i]);
	}
}

 

Mblaze_iic -> I2C_lcd결과 영상 

btn_intc(myip), iic(myip) 끝!