ARM-LED, Button, LCD

Park SeungChan·2024년 4월 27일
0

ARM

목록 보기
3/7

LED driver

#include "stm32f4xx_hal.h"

typedef struct { // LED포트와 핀에 대한 정보를 구조체로 선언
	GPIO_TypeDef* GPIOx;
	uint16_t GPIO_Pin;
}led_t;

void led_init(led_t *led, GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) // 사용할 LED 초기화
{
	led->GPIOx = GPIOx;
	led->GPIO_Pin = GPIO_Pin;
}

void led_on(led_t *led) // hal driver를 통해 LED ON
{
	HAL_GPIO_WritePin(led->GPIOx, led->GPIO_Pin, SET);
}

void led_off(led_t *led) // hal driver를 통해 LED ON
{
	HAL_GPIO_WritePin(led->GPIOx, led->GPIO_Pin, RESET);
}

void led_toggle(led_t *led) // hal driver를 통해 LED Toggle
{
	HAL_GPIO_TogglePin(led->GPIOx, led->GPIO_Pin);
}

LED를 사용할때는 led_t 구조체의 변수를 만들고, 변수를 초기화 한다. 그리고 driver의 함수들을 이용해 기능을 제어한다.

Button driver

Tact Switch를 사용했다.

#include "stm32f4xx_hal.h"

typedef enum {PUSHED=0, RELEASED, NO_ACT, ACT_PUSHED, ACT_RELEASED} button_state_t; // enum변수로 버튼의 동작을 정의

typedef struct { // 버튼의 포트, 핀, 이전상태에 대한 정보를 구조체로 선언
	GPIO_TypeDef* GPIOx;
	uint16_t GPIO_Pin;
	int prevState;
}button_t;

void button_init(button_t *button, GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 
{
	button->GPIOx = GPIOx;
	button->GPIO_Pin = GPIO_Pin;
	button->prevState = RELEASED; // 사용하는 tact switch는 기본적으로 Released되있음
}

button_state_t button_getState(button_t *button) // 반환값은 enum으로 선언했다.
{
	int curState = HAL_GPIO_ReadPin(button->GPIOx, button->GPIO_Pin); // 현재버튼의 상태를 hal driver로 읽어온다.
	if ((button->prevState == RELEASED) && (curState == PUSHED)) { // 누르면
		HAL_Delay(50); // debouncing
		button->prevState = PUSHED; // 눌렀기에 Push로 변경
		return ACT_PUSHED;
	}
	else if ((button->prevState == PUSHED) && (curState == RELEASED)) { // 떼면
		HAL_Delay(50);
		button->prevState = RELEASED; // 뗐기에 Release로 변경
		return ACT_RELEASED;
	}
	return NO_ACT;
}

버튼은 3가지 값을 반환(NO_ACT, ACT_PUSHED, ACT_RELEASED)하므로 이것을 이용해 버튼을 제어한다.

LCD driver

I2C LCD driver다.

LCD 뒷면에 PCF8574 I/O expander가 장착되있다. 역할은 MCU와 I2C통신을 통해 데이터를 주고받고, LCD에 데이터를 8핀으로 확장시켜 작동하도록 도와준다.

PCF8574의 0번핀부터 7번핀은 위 그림처럼 구성되있다.

LCD에 쓰기 동작만 하기에 R/W = "L" write mode로 한다.

I2C protocol : 시작신호를 보내고 7bit의 주소를 보낸다. A2,A1,A0는 default로 1이다. R/W는 write mode이므로 0이고, R/W까지 전송되면 Ack신호를 보낸다. 이후 8bit data와 Ack신호를 보내고, 통신이 끝났으면 stop한다.

I2C LCD는 4bit interface를 사용한다. 2번에 나눠서 전송되며, 먼저 상위 4bit를 전송하고, 두번째로 하위 4bit를 전송한다.

데이터를 쓰기위한 Timing Diagram이다. RS와 R/W신호를 준비하고 Enable이 High에서 data를 준비하고, Enable이 Low로 edge되는 순간 LCD에 write된다.

4bit interface에서 초기화를 위한 순서다.

#include "stm32f4xx_hal.h"

#define LCD_RS					0
#define LCD_RW					1
#define LCD_E					2
#define LCD_BACKLIGHT			3
#define LCD_4BIT_FUNCTION_SET	0x28
#define LCD_DISPLAY_OFF			0x08
#define LCD_DISPLAY_ON			0x0C
#define LCD_DISPLAY_CLEAR		0x01
#define LCD_ENTRY_MODE_SET		0x06

#define lcdDevAddr				0x27 // 기기의 주소
#define lcdDevAddr_w			(0x27<<1)

extern I2C_HandleTypeDef hi2c1;
uint8_t lcdData = 0;

void LCD_init() // Reset function에 쓰여진대로 진행
{
	LCD_delay(15);
	LCD_cmdMode();
	LCD_writeMode();
	LCD_sendHighNibble(0x30);
	LCD_delay(5);
	LCD_sendHighNibble(0x30);
	LCD_delay(1);
	LCD_sendHighNibble(0x30);
	LCD_sendHighNibble(0x20);
	LCD_sendByte(LCD_4BIT_FUNCTION_SET); // Function Set : 4bit interface, 2line, 5x8font
	LCD_sendByte(LCD_DISPLAY_OFF); // Display Off
	LCD_sendByte(LCD_DISPLAY_CLEAR); // Display Clear
	LCD_sendByte(LCD_ENTRY_MODE_SET); // Entry Mode Set

	LCD_sendByte(LCD_DISPLAY_ON); // Display On
	LCD_backLightOn(); // BackLight On
}

void LCD_backLightOn()
{
	lcdData |= (1<<LCD_BACKLIGHT);
	LCD_sendData(lcdData);
}

void LCD_delay(uint32_t Delay)
{
	HAL_Delay(Delay);
}

void LCD_sendData(uint8_t data)
{
	HAL_I2C_Master_Transmit(&hi2c1, lcdDevAddr_w, &data, 1, 1000);
}

void LCD_cmdMode() // 0 명령어 모드
{
	lcdData &= ~(1<<LCD_RS);
}

void LCD_charMode() // 1 데이터 모드
{
	lcdData |= (1<<LCD_RS);
}

void LCD_writeMode() // 쓰기 모드
{
	lcdData &= ~(1<<LCD_RW);
}

void LCD_E_High()
{
	lcdData |= (1<<LCD_E);
}

void LCD_E_Low()
{
	lcdData &= ~(1<<LCD_E);
}

void LCD_sendHighNibble(uint8_t data) // 8bit data중에 상위 4bit를 timing diagram에 따라 수행
{
	LCD_E_High();
	lcdData = (lcdData & 0x0f) | (data & 0xf0); // higher 4bit data
	LCD_sendData(lcdData); // MCU send data to LCD
	HAL_Delay(1);
	LCD_E_Low();
	LCD_sendData(lcdData); // MCU send data to LCD, enable low를 보내기 위해
	HAL_Delay(1);
}

void LCD_sendLowNibble(uint8_t data) // 8bit data중에 하위 4bit를 timing diagram에 따라 수행
{
	LCD_E_High();
	lcdData = (lcdData & 0x0f) | ((data & 0x0f) << 4); // lower 4bit data
	LCD_sendData(lcdData); // MCU send data to LCD
	HAL_Delay(1);
	LCD_E_Low();
	LCD_sendData(lcdData); // MCU send data to LCD
	HAL_Delay(1);
}

void LCD_sendByte(uint8_t data)
{
	LCD_sendHighNibble(data); // higher 4bit
	LCD_sendLowNibble(data); // lower 4bit
}

void LCD_writeCmdData(uint8_t data)
{
	LCD_cmdMode();
	LCD_writeMode();
	LCD_sendByte(data);
}

void LCD_writeCharData(uint8_t data)
{
	LCD_charMode();
	LCD_writeMode();
	LCD_sendByte(data);
}

void LCD_gotoXY(uint8_t row, uint8_t col) // LCD를 표시하는 위치설정
{
	col %= 16;
	row %= 2;

	uint8_t lcdRegAddr = (0x40 * row) + col;
	uint8_t command = 0x80 + lcdRegAddr;
	LCD_writeCmdData(command);
}

void LCD_writeString(char *str) // 문자열 LCD에 출력
{
	for (int i=0; str[i]; i++) { // NULL을 만날때까지 진행
		LCD_writeCharData(str[i]);
	}
}

void LCD_writeStringXY(uint8_t row, uint8_t col, char *str)
{
	LCD_gotoXY(row, col);
	LCD_writeString(str);
}

Caller

Caller : 부르는 함수
Callee : 불려지는 함수
main함수 안에 func함수가 있다면 main은 Caller, func는 Callee가 된다.

profile
RTL Circuit Design & Verification

0개의 댓글