일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- KEYPAD
- structural modeling
- LED
- dataflow modeling
- ATMEGA128A
- verilog
- Pspice
- DHT11
- ring counter
- test bench
- half adder
- stop watch
- Linked List
- behavioral modeling
- FND
- Algorithm
- soc 설계
- uart 통신
- BASYS3
- hc-sr04
- vivado
- D Flip Flop
- gpio
- java
- Edge Detector
- pwm
- i2c 통신
- atmega 128a
- Recursion
- prescaling
- Today
- Total
거북이처럼 천천히
Data Direction Register의 비트 단위로 Control 본문
Data Direction Register의 비트 단위로 Control
유로 청년 2024. 5. 26. 19:051. 환경 : Microchip Studio
2. 목표 :
- DDR 전체를 출력/입력으로 설정하는 것이 아닌 Bit 단위로 설정하여 효율적으로 LED를 출력하도록 설계
- 구조체(Structure)를 이용하여 구현함으로서 구조체의 기본 지식 정비 및 기본 활용 공부
3. 큰 그림:
- 이전까지는 DDRD = 0xff로 선언하여 PORTD의 8핀 전체를 출력으로 사용하거나
DDRF = 0x00으로 선언하여 PORTF의 8핀 전체를 입력으로 사용했다. - 하지만, 이는 비효율적으로 하드웨어를 제어 하는 것이기 때문에 bit 단위로 입력/출력을 설정함으로서
효율적으로 하드웨어를 제어하도록 설계하겠다. - ex) DDRD = 0x18
→ PORTD에서 3번째, 4번째만 출력으로 사용하겠다는 의미 - 구조체를 정의하여 구조체 내에 1) PORT의 메모리 주소, 2) 출력하고자 하는 핀 넘버를 갖고 있다.
4. Source code
● 구현 목표 : 0핀 -> 7핀 -> 0핀으로 Shift 연산자를 사용하여 출력
● 비록 같은 출력이지만, 약간씩 소스를 변형하여 구현하도록 하겠다.
4.1) DDR(Data Direction Register) 제어를 통해 특정 bit에서만 출력
#include "personal.h"
int main(void)
{
uint8_t data = PORTD1;
while (1)
{
for(uint8_t i=0; i<8; i++) {
init_DDR(i);
left_shift(&data);
}
for(uint8_t i=6; i>0; i--) {
init_DDR(i);
right_shift(&data);
}
}
}
#include "personal.h"
// Initialization Data direction register.
void init_DDR(uint8_t pin_number) {
// DDRD 레지스터를 7번 shift하여 초기화 한뒤,
// pin_number 핀만 출력으로 설정
LED_DDR = (LED_DDR<<8) | (DDD1<<pin_number);
}
// Left Shift
void left_shift(uint8_t *data) {
// PORTD의 8핀에 High-level signal을 주었음에도
// DDRD는 특정 bit에서만 출력이 되도록 설정했기 때문에
// 특정 bit에서만 출력이 발생한다.
*data = 0xff;
GPIO_Output(*data);
}
// Right Shift
void right_shift(uint8_t *data) {
*data = 0xff;
GPIO_Output(*data);
}
// GPIO output
void GPIO_Output(uint8_t data) {
LED_PORT = data;
_delay_ms(TIME);
}
#ifndef PERSONAL_H_
#define PERSONAL_H_
// Include common.h header file.
#include "common.h"
// Substitute constant
#define LED_DDR DDRD
#define LED_PORT PORTD
#define TIME 200
// Function prototype
void init_DDR(uint8_t pin_number);
void left_shift(uint8_t *data);
void GPIO_Output(uint8_t data);
void right_shift(uint8_t *data);
#endif /* PERSONAL_H_ */
#ifndef COMMON_H_
#define COMMON_H_
// Define | Initialization.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#endif /* COMMON_H_ */
※ 하지만, 여기서 PORTD = 0xff으로 작성할 필요가 있는가?
어차피 출력되는 핀은 하나에 불과한데? 그래서 PORTD 의 값을 아래와 같이 수정할 수 있다.
#include "personal.h"
// Initialization Data direction register.
void init_DDR(uint8_t pin_number) {
// DDRD 레지스터를 7번 shift하여 초기화 한뒤,
// pin_number 핀만 출력으로 설정
LED_DDR = (LED_DDR<<8) | (DDD1<<pin_number);
}
// Left Shift
void left_shift(uint8_t *data, uint8_t pin_number) {
//
*data = (*data<<8) | (PORTD1<<pin_number);
GPIO_Output(*data);
}
// Right Shift
void right_shift(uint8_t *data, uint8_t pin_number) {
*data = PORTD1<<pin_number;
GPIO_Output(*data);
}
// GPIO output
void GPIO_Output(uint8_t data) {
LED_PORT = data;
_delay_ms(TIME);
}
※물론 위와 같이 수정하면 그에 따른 함수 원형 선형, main.c 파일 수정도 이루어져야 한다.
4.2) 구조체(Structure)의 개념을 도입하여 구초제의 맴버로 1) PORT의 레지스터 주소, 2) 핀 넘버를 삼고, LED를 제어
#include "personal.h"
int main(void) {
LED led;
led.port = &PORTD;
while(1) {
for(uint8_t i=0; i<8; i++) {
led.pinNumber = i;
init_DDR(&led);
left_shift(&led);
}
for(uint8_t i=6; i>0; i--) {
led.pinNumber = i;
init_DDR(&led);
right_shift(&led);
}
}
}
#include "personal.h"
// Initialization DDR(Data Direction Register)
void init_DDR(LED *led) {
// Data Sheet에서 Register Summary 파트를 보게 된다면
// 각각의 PORT들은 각각 3개의 Register를 갖는다.
// 1) PORT(PORT의 출력값을 담는 레지스터)
// 2) DDR (Data Direction 레지스터)
// 3) PIN
// 각각의 레지스터들은 위 순서대로 연속적으로 존재하며, 이 때문에 각각의 레지스터의 주소값의 차이 1씩난다.
// 자세한 내용은 Atmega128a 461 ~ 463참고
*(led->port - 1) = (*(led->port -1)<<8) | (1<<led->pinNumber);
// 위 내용은 *(led->port-1)의 의미는 다음과 같다.
// - 특정 PORT의 레지스터 주소 - 1 = 특정 PORT의 DDR
// - *(led->port - 1)은 특정 PORT의 "DDR의 메모리 주소를 통해 DDR의 값을 수정하겠다."는 의미
// - (1<<led->pin_number)은 "Shift 연산자를 통해 특정 bit만 출력으로 사용하겠다"는 의미
}
// Left shift
void left_shift(LED *led) {
// *(led->port) == PORTD
// (1<<led->pinNumber) == 1<<pinNumber
*(led->port) = (1<<led->pinNumber);
_delay_ms(TIME);
}
void right_shift(LED *led) {
*(led->port) = PORTD1<<led->pinNumber;
_delay_ms(TIME);
}
#ifndef PERSONAL_H_
#define PERSONAL_H_
// Include "common.h" header file.
#include "common.h"
// Structure
typedef struct{
volatile uint8_t *port; // LED가 연결된 포트.
uint8_t pinNumber; // LED가 연결된 핀 번호.
}LED;
// Substitute constant.
#define LED_DDR DDRD
#define LED_PORT PORTD
#define TIME 200
// Function prototype.
void init_DDR(LED *led);
void left_shift(LED *led);
void right_shift(LED *led);
#endif /* PERSONAL_H_ */
#ifndef COMMON_H_
#define COMMON_H_
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#endif /* COMMON_H_ */
※ 아래는 보는 것과 같이 Atmega 128A의 Data sheet을 보면 각 포트는 3개의 레지스터를 가진다.
1) PORT(PORT의 각 핀의 출력을 제어하는 레지스터)
2) DDR (Data Direction Register, PORT의 각 핀을 입력/출력으로 사용할지 여부를 결정하는 레지스터)
2) PIN
※ 그래서 위 코드에서 &PORT - 1 = &DDR or &DDR - 1 = &PIN 형식으로 접근이 가능하다.
※ LED 구조체는 총 2개의 맴버를 갖는다.
- volatile uint8_t * port (PORT 레지스터의 주소)
- uint8_t pin_number (특정 pin_number)
※ volatile 은 "휘발성 있는" 뜻으로 해당 키워드를 작성한 변수에 대해서 최적화 작업을 하지 않고,
항상 메모리에 접근한다.
-> 자세한 내용은 해당 홈페이지에서 참고.https://luna-archive.tistory.com/2
※ PORT의 레지스터 주소를 통해 1) PORT의 출력 뿐만 아니라 2) PORT의 레지스터 주소 - 1을 하여 DDR에 접근하여
PORT의 8핀의 입출력을 제어 할 수 있다.
5. 구현 영상
6. 마무리
- 구조체를 사용한 거 뿐만 아니라 각 PORT가 갖고 있는 레지스터의 주소를 이용하여
1) PORT의 출력 값 제어, 2) PORT의 레지스터 주소 - 1 을 통해 얻은 DDR 레지스터로 8핀의 입출력 제어 하였다.
- 조금 복잡하고, 어렵다고 생각할 수 있지만, 계속 봐보고 시간이 있으면 구현 해봄으로서 익숙해지자.
'Embedded Programming (AVR) > Atmega 128A (실습)' 카테고리의 다른 글
GPIO - LED control (3) (0) | 2024.05.29 |
---|---|
Atmega 128A의 버튼 (기초) (0) | 2024.05.26 |
궁금증 정리 노트 - Atmega128A (0) | 2024.05.25 |
Pointer (0) | 2024.05.25 |
Separate Compilation (0) | 2024.05.25 |