C 코드 최적화의 필요성

- 임베디드 CPU 한계 : 저전력, 저가로 성능 제한

 -> 제한된 자원으로 좋은 성능을 내기위해 필요

+ CPU 구조, 어셈블러, 컴파일러에 대한 이해 필요

 

일반적인 최적화 방법

- 나눗셈, 나머지 연산을 피함

- 반복을 최적화

- 메모리 보다 빠른 레지스터 사용

- 함수 호출 방식을 최적화

 

AVR core 구조

- 폰노이만 : 데이터, 프로그램 메모리를 하나의 버스로 사용

- 하버드 : 데이터, 프로그램 메모리를 분리 -> 동시에 명령어와 데이터를 읽고쓰기 가능

 -> AVR core는 하버드 아키텍처를 따름

 

CPU 연산 과정

- fetch :  메모리서 명령어 가져옴

- decode : 명령어에 있는 레지스터 해석

- execute : 실행

 

AVR core

- single level pipelining 사용

- 한 명령어가 실행되는 동안 다음 명령어가 pre fetch됨

 -> 매 클럭 사이클마다 명령어가 실행

- 32개의 8비트 범용 레지스터를 가지고 있음. 이를 잘 활용해서 최적화

- R0 ~ R25 : 데이터 레지스터로 사용

- R26 ~ R31 : 주소 레지스터로 사용

 

 

 

AVR 컴파일러

- AVR studio : gcc 사용. avr-gcc

- code vision : 상용 컴파일러 사용

 

 

AVR GCC 최적화 옵션

- 옵션에 따라 코드 크기, 성능 최적화 레벨 지정 가능

  ex) -O0, -O1, -O2, -O3, -Os

    => avr-GCC -O3 main.c

  *-O0 는 최적화 x, -O3은 가장 높은 성능, -Os는 코드 사이즈 가장 줄임

 

 

 

데이터에 따른 최적화

- 타입에 따른 최적화 : 가능한 크기가 작은 타입 사용

- 범위에 따른 최적화 : 같은 크기더라도 전역보다 지역변수가 유리.

* atmega128에선 전역변수는 SRAM(16비트 어드레스모드)에 할당.

   -> 변수 크기가 8비트 크기여도 16비트 크기 어드레스로 할당하여 공간 낭비

 

static을 이용한 최적화

- 전역 변수에 static 사용시 선언된 파일 내에서만으로 범위 제한.

- 지역 변수의 경우 static 사용 안하는것이 좋다.

 

어셈블리어를 사용한 최적화

- 적당히 사용하면 좋으나 코드 이식성을 떨어지게함.

- 인라인 어셈블리 : C코드 일부를 어셈블리어로 사용

ex : #define sei() __asm__ __volatile__("sei"::)

- __asm__ : 인라인 어셈블리를 의미하는 지시어

- __volatile__ : 최적화에 의해 없어지는걸 방지

- sei : SREG 레지스터의 I flag(전역 인터럽트 플레그)를 셋

  일반 인라인 어셈블리
C코드 #include <avr/io.h>
void enable_usart_rx(void)
{
  UCSR0B |= 0x80;
};

int main()
{
  enable_usart_rx();
  while(1){
  }
}
#include <avr/io.h>
#define enable_usart_rx() \
  __asm__ __volatile__(\
  "lds r24, 0x00C1" "\n\t" \
  "ori r24, 0x80" "\n\t" \
  "sts 0x00C1, r24" \
  ::)
int main()
{
  enable_usart_rx();
  while(1){
  }
}
AVR 코드 메모리 사용량 296bytes 226bytes
최적화 옵션 -Os -Os

 

Cycle Counter

- 어셈블리 명령어가 실행된 회수

- 실행이 끝난 후 값 = 총 실행된 명령어 회수

 

임베디드 시스템 개발

- PC랑 달리 용도가 정해지고, 가격, 전력을 고려해서 HW와 SW를 제한해야 함.

- 호스트 PC에서 타겟 디바이스에 맞게 컴파일 후 올려서 동작 시킨다

- Host PC에서 하는 일 : 개발 툴(교차 컴파일러, 디버거 등), 시뮬레이션을 위한 타겟 장치 시뮬레이터 등

- 부트로더 개발 : 타겟 장치에 전원 인가시 부팅 롬에서 가장 먼저 실행. HW 초기화, 커널 로딩, JTAG 인터페이스로 설치

- 커널과 파일 시스템 : 호스트 PC의 개발 환경에서 만듬, 이미지 형태로 부트롬이나 다른 곳에 저장. 

- 임베디드 응용프로그램 : 커널과 파일 시스템위에서 동작.

 

 

 

부트로더

- HW 초기화, OS 커널 메모리로 로드하는 펌웨어

- LILO Linux LOader : 특정 파일시스템에 의존 x, 최대 16개 이미지 시동시 선택 가능, MBR이나 파티션 부트섹터에 위치

- GRUB GNU GRUB : GNU의 부트로더, 동적으로 커널 인자 조정 가능, 다양한 실행 파일 지원, 명령줄 인터페이스 제공

- BLOB BootLoader OBject : 선택 부팅 가능, MBR에서 동작하는 다른 부트로더와는 달리 플레시 메모리 0블록에서 동작

- U-BootUniversal Bootloader : PPC와 ARM 기반의 임베디드 부트로더, ARM, x86, MIPS 등 CPU 지원, 오픈소스

- NTLDR NT Loader : 윈도우 NT 시동 부트로더, 하드디스크 이외의 USB 플래시 드라이브 등 휴대용 장치서도 실행 가능

 

 

 

U-Boot Universal Bootloader

- 다양한 시스템에서 사용가능한 부트로더로 시리얼, 네트워크, USB, Flash 등 지원

- 다양한 아키텍처와 디바이스를 지원하므로 타겟 보드에 맞게 포팅 필요

- 동작 흐름 : Start.s 시작

   * Start.s : CPU 초기화(인터럽트 마스크, 클럭 설정), DRAM 초기화, 플래시 메모리 코드를 DRAM으로 다른 초기화작업

 

https://julrams.tistory.com/category/System%20Programming/Tips?page=4

 

U-boot 구성

- /api : API 코드, 예제

- /arch : 아키텍처 별 code, lib, 시작코드 start.S

- /common : 아키텍처에 독립적인 파일과 명령어

- /drivers : 외부 장치 드라이버들

- /fs : uboot의 파일 시스템 관련

- /include : 헤더  파일

- /net : network 관련

- build.sh : 빌드 스크립트

- Makefile : 빌드 설정 Makefile

ref : https://kergoo.wordpress.com/2017/04/09/u-boot-porting-guide/

 

 

주변장치 

1. UART와 USART

2. GPIO

3. JTAG, SWD

4. I2C(TWI)

5. PWM

6. SPI

7. Flash Memory

8. Ehternet

9. USB

10. CAN

 

 

7. Flash Memory

- 데이터 읽고 쓰는 비휘발성 메모리,종류 : Nand/Nor Flash Memory

- Nand Flash Memory : 싸서 대용량으로 많이 쓴다

- eMMC Embedded Multi Media Controller : Nand Flash Memory + 메모리 컨트롤러 + 멀티미디어 카드 + BGA 캡슐화

  => 데이터 전송 속도와 처리 량 개선하여 모바일 장치에서 많이 쓴다

 

8. Ethernet

- 이더넷.. OSI 7계층 데이터링크 인가, 물리계층인가 쯤에 있던거같은데 찾아보니 물리 계층이더라.

- MAC 주소로 호스트 간 데이터 주고받는 네트워크 약속, 유선은 이더넷, 무선은 802.11(와이파이)

* 물리 계층 : 신호와 배선 규칙(이더넷, RS-232C, 802.11),  데이터 링크 계층 : 프로토콜 형식 정의(MAC)

* 인터넷과 이더넷 ? : 인터넷은 네트워크(교통), 이더넷은 네트워크를 이루는 컴퓨터들의 통신 수단 중 하나(도로)란다.

ref: https://namu.wiki/w/%EC%9D%B4%EB%8D%94%EB%84%B7

 

이더넷 장치 블록 다이어그램

 

9. USB Universal Serial Bus

- 배경 : 인텔, 마소, IBM 등 주변장치와 PC 간 인터페이스 통일, 쉽고 빠르며 작은 인터페이싱을 위해 개발

- 특징 : 버스 자체 전원 공급(+5v, 500mA), 소형 커넥터, PnP 지원 등

ref : https://blog.daum.net/insopack77/23

 

 

- 용어 정리 ref : https://m.cafe.daum.net/banghakme/9XyX/167

   - USB 호스트 : USB 호스트 컨트롤러, USB 시스템 소프트웨어, 클라이언트로 구성하여 USB 디바이스 접근 제어.

   - USB 디바이스 : USB 인터페이스, USB 논리 디바이스, Function 으로 구성.

- USB 데이터 전송 구조

   - Client SW : OS와 USB 디바이스에 따라 제공되는 SW

   - USB System SW : OS에서 USB를 지원하는 SW

   - USB 호스트 컨트롤러 : 호스트 측 버스 인터페이스로 USB 디바이스를 호스트와 결합시켜주는 모든 SW/HW 

 

-  USB에 대한 추가적인 자세한 동작 원리는 아래 링크

ref : https://m.blog.naver.com/prnbada7/221215493534


USB 데이터 전송 구조

물리적인 버스구조

 

 

 

10. CAN Controller Area Network

- 배경 : 차량 내 ECU Electronics Control Unit 간 통신에 1:1 방식인 UART가 적합하지 않아 다중 통신용으로 개발.

- 용도 : 차량 내부 계측 제어 장비들간 직렬 통신할 수 있도록 개발된 네트워크.

- 특징 : 멀티 마스터 통신, 선 2개면 충분(CAN_high, CAN_Low), 잡음에 강함, 고속 원거리 통신 등

- CAN FRAME : SOF 시작비트, ID, Control 데이터길이, Data 데이터, CRC 오류 검출, ACK 오류없이 ok, EOF 끝비트

 

 

 

주변장치 

1. UART와 USART

2. GPIO

3. JTAG, SWD

4. I2C(TWI)

5. PWM

6. SPI

7. Flash Memory

8. Ehternet

9. USB

10. CAN

11. 무선 장치

 

 

 

1. UART Universal Asynchronous Receiver/Transmitter

- 범용으로 병렬 데이터를 직렬화 하여 비동기적으로 송수신하는 규격(인터페이스) (* 병렬 통신 : 버스)

- USB2Serial 디버그, 블루투스, Rs-232/422 등 용도로 사용

- RS-232, RS-422와 같이 사용됨(*UART는 직렬 통신 규칙, RS-232는 하드웨어 규칙)

ref : https://treeroad.tistory.com/entry/RS232%EC%99%80-UART

- 위 링크보다는 이게 더 정확한 설명인거 같다.

ref : https://ko.serial-cable.com/info/difference-of-uart-serial-and-rs232-serial-30244548.html

- USART의 경우 동기 직렬 통신도 지원

- 시작비트, 데이터 비트(5 ~ 8비트), 패리티비트(필수 x), 종료 비트로 구성

 

 

** TTL 레벨과 RS-232C 레벨

- TTL 레벨은 0v, 5v로 0,1 표현 <-> RS-232C 레벨은 +-5V 양, 음전압으로 표현하여 노이즈에 강인

ref : https://treeroad.tistory.com/entry/%EC%99%9C-TTL-%ED%86%B5%EC%8B%A0%EC%9D%B8%EA%B0%80

 

 

2. GPIO General Perpose Input Output 

- 범용으로 사용 가능한 입출력 포트

- 디스플레이, 센서, LCD, USB Host, CAN 등 통신 인터페이스로도 사용

- 아래의 링크는 UART GPIO로 시리얼 통신 예시

ref : https://codedosa.com/470

 

 

 

3. 디버그 인터페이스

- JTAG Joint Test Action Group : 플레시 메모리에 소스코드를 다운로드/업로드하는 경우, 디버깅 용도로 사용

  * TDI 테스트 데이터 입력, TDO 테스트 데이터 출력, TCK 테스트 클럭, TMS 테스트 모드, TRST 테스트리셋

- SWD Serial Wired Debug : 직렬 통신이다보니 클럭 SWCLK와 데이터 SWDIO 핀 2개 사용

 

 

 

 

4. I2C Inter Integrated Circuit(TWI Two Wire Interface라고도 함)

- 마이크로 프로세서와 저속 주변장치 간 통신을 위한 규격

- 양방향 선 2개 SCL Serial Clock과 SDA Serial Data 이용. 마스터-슬레이브 구성으로 동작

-> 하드웨어적으로 간단, 수 많은(최대 127개) 노드(슬레이브) 연결, UART와 달리 클럭을 사용해서 속도 안정해도됨

5. PWM 

- 한 주기당 펄스의 폭, 듀티비를 조절하여 만들어 낸 신호

- DC 모터 제어 뿐만 아니라 LED 밝기, 밸브, 유압, 기계 장치 등 많은 분야에서 사용

6. SPI Serial Peripheral Interface

- 모토로라에서 개발한 전이중 동기 통신 규격

- I2C 방식과 비슷하게 마스터-슬레이브 방식 다만 전이중 통신

- SCLK : 시리얼 클럭

- MOSI : 마스터 출력 슬레이브 입력

- MISO : 마스터 인풋, 슬레이브 아웃풋

- SS : 슬레이브 셀렉트

SPI 인터페이스와 데이터 포멧

 

 

 

 

5. 프로세서 아키텍처로 분류

- 프로세서 제조사 or IP 회사로 분류

- x86 : 인텔의 32비트 아키텍처 PC에서 많이 쓰임. 레지스터가 8개로 부족

- amd64 : amd에서 만든 x86을 확장한 64비트 아키텍처. 레지스터를 16개로 늘어남. 가상 메모리, 물리메모리 크게늘어남

- arm : 초기 저전력 soc를 만들다가 고성능 저전력 프로세서 설계   ex) cortex-A(응용어플)/R(rtos 최적)/M(mcu 최적)

   + ARM 설계를 따르는 프로세서 : 퀄컴 스냅드래곤, NVIDA 테그라, 삼성 엑시노스

- mips : 스탠포드에서 RISC로 개발. 비디오 콘솔, 멀티미디어 같이 높은 연산 처리가 필요한 곳에 사용.

 

 

ARM RISC

- 초기 ARM7/9/11 같은 저전력 프로세서 위주였으나 스마트폰 대중화후 고성능 Cortex 아키텍처 시리즈

- RISC 특징 : load-store 구조, 고정 길이 32비트 명령어, 주소 명령어 구조(1개 명령어는 2개 소스 주소 + 1개 목적지)

* 로드-저장 구조 : CISC에서 memory to memory(메모리서 레지스터로 가져와 메모리로 저장) 연산을 하는것과 달리 레지스터에 로드 저장, 메모리 사용 x

- ARM RISC

  - 32비트 프로세서여도 16비트 명령어 사용가능(Thumb 모드) -> 코드 크기 축소

  - 조건부 명령어 구조 : 모든 명령어에 조건부 추가 가능

  - 최적화된 레지스터 갯수: 일반 RISC CPU가 많은 레지스터를 사용해 공간 낭비, ARM RISC는 필요한 레지스터만 사용

 

 

어플리케이션 프로세서 AP

-  모바일 기기에 사용되는 프로세서로 PC에 사용되는것과는 다름. 스마트폰이 나오면서 OS, 게임, 멀티미디어가 중요해져 어플리케이션에 특화하는 쪽으로 설계되 AP라 부름.

- ex : 퀄컴 스냅 드래곤, 삼성 엑시노스, 엔디비아 테그라

퀄컴 스냅드래곤

 

 

ARM Cortex

- 초기 저전력 위주 ARM7/9/11 설계에서 용도별로 구분한 시리즈

- ARM Cortex-A : Non-RTOS와 어플리케이션에 특화된 프로세서

- ARM Cortex-R : RTOS와 실시간 시스템에 특화됨

- ARM Cortex-M : 저가, 저전력에 특화된 MCU

 

 

 

 

ARM 아키텍처

- 다시 정리하면 초기에는 저전력 프로세서 위주였으나 용도 별로 특화된 coretex 시리즈가 나옴

- ARM RISC : 32비트 프로세서에서도 16비트 명령어 지원(thumb 모드), 조건부 명령어, 최적 레지스터 수

시리즈 아키텍처 버전 코어 기능
ARM1 ARMv1 ARM1 캐시 x
ARM2 ARMv2a ARM2 곱샘 명령 추가, 4MIPS, 8Mhz
ARM3 ARMv2a ARM2a ARM 프로세서서 최초로 캐시 사용 4k, 7MIPS, 12MHz
ARM6 ARMv3 ARM60 최초 32비트 메모리 지원, 10 MIPS, 12MHz
ARM7 ARMv3 ARM700 8k 캐시, 40MHz
ARM7500FE Soc, FPA와 EDO 메모리 커트롤러 추가(FE), 56MHz
ARM8 ARMv4 ARM810 5단계 파이프라인, 고정된 예측 분기, 84MIPS, 72MHz
ARM9TDMI ARMv4 ARM9TDMI 5단계 파이프라인
ARM9E ARMv5TE ARM946E-S 향상된 DSP, 가변 메모리 밀착형 MPU
ARM11 ARMv6 ARM1136J-S SIMD(단일 명령어 다중 데이터), 8단계 파이프라인 532-665MHz

 

 

 

ARM Cortex-A 아키텍처

- 스마트폰에 적합한 고성능 아키텍처, 이 아키텍처 + 메모리 + 제어기를 한칩 SoC에 만들어 사용

시리즈 아키텍처버전 코어 기능
Cortex-A ARMv7-A Cortex-A5 저전력 초소형 프로세서, 8단계 싱글 파이프라인
    Cortex-A7 멀티코어, 1.2GHz (ex : 엑시노스 5410)
    Cortex-A9 저전력 최적 멀티코어 프로세서(많이씀) (ex:엔비디아 테그라2)
    Cortex-A15 Armv7-A 중 가장 고성능
    Cortex-A17 Big litttle 지원
  Armv8-A Cortex-A32 32비트 기반 가장 작은 공간, MCU 소형 임베디드 시스템 중 고성능
    Cortex-A35 64비트 Armv8-A
    Cortex-A65AE 차량용 멀티스레드 Cortex-A CPU
    Cortex-76AE 자율주행차 같은 안전 어플리케이션용
    Cortex-A77 5G 솔루션, 고성능

 

 

ARM Cortex-A9 프로세서

- ARMv7 + 다중 슈퍼스케일러, 비순차 추론 8단계 파이프라인을 이용한 고성능 프로세서

- 슈퍼 스케일러 파이프라인 : 최대 성능, 저전력

- Neon 엔진 : 미디어, 신호 처리 가속화 엔진

- 부동 소수점 처리장치 : 이전 ARM FPU 성능 2배 개선

- Thumb-2 : 기존 코드 성능을 유지하며 메모리양 최대 30% 절감

- Trust Zone : 보안 어플리케이션 안전 실행 보장

- L2 캐쉬 컨트롤러 : 2MB 캐쉬 메모리까지 저 지연, 고 대역 액세스 제공

 

임베디드 시스템 개발 프로세스

- 분석 -> 설계 -> 개발 -> 테스트 -> 양산 -> 유지 보수

 

 

1. 분석

- 수요/기술 조사, 요구사항 평가, 사양, 비용, 디자인 등 정리

- output : 제품 기획서, 요구 명세서, 디자인 설계서

 

2. 설계

- 하드웨어 회로/기구/소프트웨어 설계

- output : 회로도, 기구설계도면, 소프트웨어 흐름 차트, UI 설계서

회로도 기구설계도
UI 설계서 소프트웨어 흐름도

 

 

3. 개발

- 하드웨어 PCB(printed circuit board), PBA(printed board assembly)  개발 후 SW 개발 디버깅 진행

- output : PCB, PBA, BOM, 아트웍 데이터, 소스코드

 

4. 테스트

- 기능, 신뢰성, 성능등을 시험

- ex : 유닛 테스트, HW 테스트,  통합 테스트, 필드 테스트, 환경 테스트

 

5. 양산 : 시제품 양산 - 부품 구입, PCB/SMT/기구 제작 및 포장

6. 유지보수 : 업데이트, AS

 

 

 

 

임베디드 프로세서 분류 방식

1. 명령어(시스템 구조)에 의한 분류

2. 버스 크기에 의한 분류

3. CPU 연산 장치 개수 분류

4. 아키텍처 분류

5. 용도에 따라 분류

 

 

1. 명령어(시스템 구조로 분류)

- CISC Complex Instruction Set Computer : 명령어가 길고 복잡하고 실행 사이클도 다양한 컴퓨터. 적은 명령어 사용

   ex) Interl x86(78년 인텔 8086에 사용된 아키텍처와 후속작), AMD64(x86의 64bit 확장 명령어셋 amd가 99년 발표)

- RISC Reduced ISC : CISC의 단점 개선. 명령어가 짧고, 간단, 빠름. 그렇다고 성능이 좋은건 아니며 명령어 많이 필요.

   ex) ARM, AArch64(arm의 64비트 확장 명령어셋) , RISC-V(버클리서 만든 무료 오픈소스 RISC ISA)

 

2. 데이터 버스 크기에 따른 프로세서

- 버스 크기가 클수록 많이 처리해서 좋지만, 용도나 비용, 전원 소모 등에 따라 적은 비트의 프로세서도 여전히 많이씀

- 4비트 : Intel 4004 71년 인텔에서 출시한 4비트 CPU 최초의 마이크로 프로세서

- 8비트 : PC에 사용된 Intel 8080(74년 출시), 모토로라 6800 등이 있고, MCU 용으로 8051(인텔), AVR(아트멜), PIC (마이크로칩)이 가전, 센서 등에 지금도 많이 씀.

- 16비트 : 8비트 보다 고성능, intel 8086이 78년 최초 제작(이후 186, 286 등). 32비트에 밀려 잘안씀.

- 32비트 : 가장 오래 사용됨 intel 80386, 80486, MIPS, ARM 등에서도 많이 출시

- 64비트 :  32비트 마이크로 프로세서의 확장. IA-64(intel과 hp가 개발), x86-64(amd64), armv8-A(arm의 64비트 ISA, 예시로 퀄컴 스냅드래곤, 삼성 엑시노스 등)

4비트 마이크로 프로세서 : 인텔 4004 8비트 마이크로 컨트롤러 : 아트멜사의 Atmega128
16비트 마이크로 프로세서 : 인텔 8086 32비트 마이크로 프로세서 : 인텔 80386

 

 

3. 코어 수로 분류

- 처음에는 ALU(산술연산장치/정수) 하나 뿐이었으나 필요에 따라 FPU(부동 소수점 연산)가 추가 되거나 여러개로 구성

 * 부동 소수점 : 소수점이 둥둥 떠다닌다. 지수에 따라서 소수점이 이동되서인듯 싶다.

- 클록 속도를 높이는 대신 코어를 높여 성능 향상. 싱글 코어, 듀얼 코어, 쿼드코어, 헥사 코어 등

 

 

 

4.용도에 따라 분류

- CPU(MPU) : pc or 워크스테이션에 사용됨. 제어기(명령어 해석 및 실행), 레지스터, ALU 등 구성

- MCU : cpu보다 저성능이지만 마이크로 프로세서에 메모리, 주변 장치 제어기, 입출력 인터페이스 내장. 

- DSP(digital signal processor) : 디지털 신호 처리를 위해 행렬 연산에 최적화됨. ex) 오디오, 영상 처리

* 컴퓨터가 실수 저장하는 방식  고정 소수점, 부동 소수점 : 10비트로 고정소수점은 1024개 수 표현 가능. 부동 소수점 방식에서 그 중 2비트를 자리수로 쓴다면 2^8(0~254) x 10^3(0, 10, 100, 1000) -> 25만개 표현 가능. 고정 소수점보다 세밀

- GPU(graphics processing unit) : 그래픽, 음성 신호를 다루기 위해 병렬처리에 특화되어 캐시 메모리가 작은 대신 수 많은 ALU가 덜 정밀하더라도 동시에 처리해 고속화됨. 

- FPU(floating point unit) : cpu 내에 부동 소수점 연산에 최적화된 논리회로

 

 

 

 

 

원래 KUT-128에는 LCD가 바로 연결되지 않고 LCD 커넥터에다가 연결하도록 되어있다.

시뮬레이터 상에서는 만들 필요 없으니 그냥 바로 LCD 를 연결시켜서 만들었다.

회로 자체 만드는건 어렵지는 않았는데,

LCD 써보려고 하니까 글이 잘 안읽히더라

조금 찾아보니까 이분이 LCD 내용을 잘 정리하셨더라.

https://kogun.tistory.com/23

 

AVR(ATmaga128)로 텍스트LCD 장난하기

안녕하세요 고군입니다~ 이번 시간에는 텍스트LCD에 대하여 알아보고 AVR을 사용하여 화면을 표시해보도록 하겠습니다. LCD는 저렴한 가격으로 인해 산업현장에서는 아직도 많이 사용되고 있으

kogun.tistory.com

 

내가 정리한것 보다는 위 링크 보는게 이해하기 좋을거같다.

 

 

LCD 핀

- RS 핀 : 0은 명령어, 1은 데이터  

- DB4~7핀 : 8비트 or 4비트 모드 사용가능한데 4비트 모드 사용시 이 4개 핀 사용

- RW 핀 : 0이 W, 1이 R

 

LCD 

- 레지스터들 : 8비트 IR(명령), DR(데이터)가 존재. RS핀으로 선택

- IR : DDRAM(디스플래이 데이터), CGRAM(문자 생성) 주소 설정,메모리클리어, 디스플래이 onoff 명령어 등 CPU가 쓴다.

- DR : DDRAM, CGRAM에 쓴 데이터나 읽은 데이터 일시 저장. DR에 쓴 데이터는 DDRAM, CGRAM에 써진다. 렘으로부터 읽을때 IR에 주소를 넣으면 DR에 들어옴

- Busy Flag BF : LCD가 명령 받을수 있는지 표현. BF가 1이면 못받음. 0이면 받을 수 있음.RS=0, RW=1이면 BF 읽음

- DDRAM display data ram : 8비트 문자 데이터 표시. 16 x 2 lcd이나 실제로는 최대 80글자 기억

- CGROM 문자 생성 롬 : 5x7, 5x10 크기의 도트 문자 보관. CGROM의 문자 코드를 DDRAM에 써주면 표시됨.

- CGRAM 문자 생성 램 : 사용자가 정의한 문자를 보관. 8개 까지.

- 어드레스 카운터 : DDRAM, CGRAM 어드레스 지정시 사용. 

 

아래의 링크에서도 정말 잘 정리되어 있으니 참고하는게 좋겠다.

https://electronicsdo.tistory.com/entry/ATmega128%EC%8B%A4%EC%A0%84-CLCD-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

https://electronicsdo.tistory.com/entry/ATmega128%EC%8B%A4%EC%A0%84-CLCD-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

 

코드는 그대로 한거같은데 무슨이유에서인지 동작하지 않는다..

 

아무래도 비트 단위로 적어가면서 확인해봐야겠다.

 

명령어 하나 당 실행 시간이 40us인데 40us를 대기하는게하나도 없다.

 

일단 이제 0617 00시 됬으니 잠시 중지

 

-----

 

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

//function set 인터페이스 길이, 표시 행 수, 문자 폰트 설정
// DL=1은 8비트, DL=0은 4비트, 1 or 2줄, 도트 크기
//   dl NF    -> DL = 0, NF : 10 -> 2 라인, 5 x 7도트 
// 0010 1000 
#define FUNCSET	0x28
//entry mode set 110
//I/D = 1 어드레스 + 1하고 커서 우측 이동
//S = 0, DDRAM에 데이터 쓴 후 화면 좌 또는 우이동?
#define ENTMODE	0x06	// entry mode set
#define ALLCLR	0x01	//all clear
//display on/off
// 0000 1100 = 0x0c
// 0000 1DCB 
// D= 1 display on, C=1 cursor on, B=1 cursor Blink
#define DISPON	0x0E	//display on
#define DISPOFF 0x04	//display off
#define LINE1	0x80
#define LINE2	0xC0	//2nd line move

void lcd_init();
//flash 는 flash 변수. flash 메모리에 저장
void lcd_string(char*);
void busy();
void command(unsigned char);
void data(unsigned char);

void main(){
	lcd_init();
	
	/*
	while(1){
		command(DISPOFF);
		_delay_us(100);
		command(DISPON);
		_delay_us(100);
	}
	*/
	
	lcd_string("hello! kut-128");
	command(LINE2);
	lcd_string("atmel atmega128");
	while(1);
}

void lcd_init(){
	DDRD = 0xff;
	PORTD &= 0xfb;
	
	_delay_ms(20);
	command(0x30);
	_delay_ms(10);
	command(0x30);
	_delay_ms(1);
	command(0x30);
	_delay_ms(1);
	command(0x20);
	_delay_ms(1);
	
	command(FUNCSET);
	command(DISPON);
	command(ALLCLR);
	command(ENTMODE);
}

void lcd_string(char *str){
	char *pstr = 0;
	
	pstr = str;
	while(*pstr)
		data(*pstr++);
}

void command(unsigned char byte){
	busy();	
	PORTD = (byte & 0xf0);
	PORTD &= 0xfe;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;
	
	
	PORTD = ((byte<<4) & 0xf0);
	PORTD &= 0xfe;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;

	/*
	busy();
	
	// 인스트럭션 상위 바이트
	PORTD = (byte & 0xf0);	//데이터
	PORTD &= 0xfe;			//rs=0
	PORTD &= 0xfd;			//rw=0;
	_delay_us(1);
	PORTD |= 0x04;			//e = 1;
	_delay_us(1);
	PORTD &= 0xfb;			//e = 0;
	
	//인스트럭션 하위 바이트
	PORTD = ((byte << 4) & 0xf0);
	PORTD &= 0xfe;
	PORTD &= 0xfd;
	_delay_us(1);
	PORTD |= 0x04;
	_delay_us(1);
	PORTD &= 0xfb;
	*/
}

void data(unsigned char byte){
	busy();
	PORTD = (byte & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;
	
	PORTD = ((byte<<4) & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;
	/*
	busy();
	
	PORTD = (byte & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_us(1);
	PORTD |= 0x04;
	_delay_us(40);
	PORTD &= 0xfb;
	
	PORTD = ((byte << 4) & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_us(1);
	PORTD |= 0x04;
	_delay_us(40);
	PORTD &= 0xfb;
*/
}


void busy(){
	_delay_ms(2);
	//_delay_us(400);
}

 

----------

지금 시간이 일요일 새벽 1시

결국 lcd 4비트 모드로 제어하려고 별짓을 다해봤지만 안됬다.

 

구글링해서 봐도 영어 실력이 부족해서 잘 읽혀지지가 않았다.

코드를 다 뜯어고쳐서 써보자니 이럴때는 아얘 다날리고 쓰는게 맞긴한데

그러기는 너무 싫더라

코드 바꾸기는 싫고 이래저래 시뮬레이션 시간, 딜레이 시간 조절해가면서 이래저래 시도해보다보니 시간이 이렇게 됬다.

 

며칠간 이걸로 한참 삽질했는데

그냥 디버깅 툴이랑 주변 장치 컨트롤러를 여러 라인에 딜레이 주는 식으로 어떻게 초기화하고 값을 입력시키는지 이해한걸로 만족해야 될거같다.

나중에 기회되면 다시 시도해야지.

 

이 다음에는 모터 시뮬도 돌리려했지만 타이머/카운터를 다루는 내용이 나온다.

이 부분은 학교 수업에서 진행하니 나중에 진도 나가고 하고 싶어지면 해야지.

 

일단 프로테우스 아트메가 시뮬레이션 글은 여기까지로 마쳐야겠다.

이 다음에는 임베디드 간단하고 넓게 정리해보고,

나빌로스 만들기 나 학교에 있는 임베디드 키트 가지고 놀아볼 생각이다.

임베디드 시스템

- 일반 PC 같은 범용 목적과는 달리 특수 목적용에 맞게 최적화되어 설계된 시스템

- 특수 목적 예시 : 가전(TV, 냉장고 등), 산업용, 특수 환경(군용, 위성)

https://jeongchul.tistory.com/90

 

임베디드 HW

1. 임베디드 프로세서

- 사용 환경에 맞게 저전력화 하거나 고사양으로 설계함.

- SoC : CPU 뿐만 아닌 타이머, 인터럽트/DMA/ LCD 컨트롤러 등을 하나의 칩에 집적하여 크기와 원가절감

- 프로세서 선정 기준 : 재고나 단종 여부, 단가, 동작 환경, 속도, 실시간성 등을 고려한다.

 

2. 임베디드 기억 장치

- 휘발성 : SRAM(고가, 고속, 캐시메모리), DRAM(저가, 상대적 저속, 저렴, 주기억장치)

- 비휘발성 : EEPROM(고가, 소용량 데이터/프로그램 저장), NAND Flash(저가, 데이터 저장)

 

3. 임베디드 입출력 장치

- IO 컨트롤러는 시리얼 혹은 네트워크로 IO 장치와 디지털 or 아날로그로 통신, CPU와는 디지털로 통신

 

 

 

임베디드 SW

- 부트로더(Bootstrap loader) : 운영체제를 주기억 장치에 적재

- 디바이스 드라이버 : 하드 디스크, 어뎁터, 모니터 등 디바이스 제어를 위한 프로그램.

 + 디바이스는 디바이스 본체와 제어기로 구성. 제어기가 디바이스 드라이브와 디바이스 사이 인터페이싱

- 운영 체제 : 자원 관리, 스케줄링

- 부트 코드 : 시스템 시작 시 하드웨어 초기화, SW 동작 설정하는 프로그램.

 

실시간 임베디드 시스템

- 실시간이면서 임베디드인 시스템. 주어진 시간 제약내에 결과를 내야 함.

- Hard 실시간 임베디드 시스템 : 시간 제약 내 처리하지 못하면 큰일나는 시스템. 무조건 지켜야함.

  ex) 미사일 제어기, 자동차 에어백 등

- Soft 실시간 임베디드 시스템 : 큰일은 나지 않지만 성능 저하

  ex) 동영상 플레이어, 통신기기

 

임베디드 운영체제 종류

- Firmware : OS를 사용하지 않은 SW. 단순한 작업이라 테스크가 적고 순위 부장이 필요없는 경우.

- 실시간 운영체제 RTOS :실시간성(정한 시간 내 결과 출력)을 보장하는 운영체제, 선점형 멀티테스킹, 모듈화와 라운드 로빈 스케쥴링 따름. 예시로 NEOS, velos 등

- Non-RTOS : 여러 태스크가 같은 자원 사용시 OS가 중재(비선점형), 예시로 Windows CE, 임베디드 리눅스 등

 

 

 

 

 

 

아날로그 비교기 추가

- 우측 아래에 가변 저항 2개가 AIN0, AIN1에 연결되어 있다.

 

아날로그 비교기 예제 1

- 풀링 방식으로 비교 후 LED shift

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>


/*
AIN1 핀 가변 저항과 AIN0 핀 가변저항 조절
AIN0 >= AIN1 -> LED 순차 점멸
아날로그 비교기 출력 ACO를 풀링으로 체크
*/
unsigned char led = 0xfe;

int main()
{
	DDRC = 0xff;
	DDRE = 0b00000010; //AIN(PE2), AIN1(PE3) 입력 설정
	ACSR = 0x0; // Analog Comparator Control and Status Registor
	SFIOR = 0x0; // Special Function IO Registor 
				 // 3번째 비트 ACME = 0 -> AIN1에 음 입력 전압
	
	PORTC = led;
	
	while(1){
		if(ACSR & 0x20){ // ACSR의 0x20 = ACO 아날로그 비교기 출력 비트가 1인지 확인
			led <<= 1;
			led |= 0x01;
			if(led == 0xff)
				led = 0xfe;
			PORTC = led;
			_delay_ms(1000);
		}
		
	}
}

 

풀링 방식은 잘 되는데,

인터럽트 방식에서는 토글이 잘 안되선지 인터럽트가 한번만 동작하고 만다. 그냥 pass

 

 

ADC도 fnd 쓰기도하고, 입력 받는데서 자꾸 워닝뜨길래 pass

 

 

 

USART

- atmega128에서는 USART 2개 내장, 클럭부, 송신부, 수신부로 구성

- 관련 레지스터로 UDR(IO 데이터 레지스터), UCSRA/B/C(제어 상태 레지스터) 3개, 보레이트 레지스터 UBRRH, UBRRL 2개로 총 6개

- UDR을 송신 데이터 버퍼 레지스터, 수신 데이터 버퍼 레지스터 두 용도로 사용, 같은 주소를 가지나 실제로는 분리되어 송신시 송신 데이터 버퍼 TXBn에 쓰고, 수신시 수신 데이터 버퍼 RXBn의 값을 읽는다.

 

USART 데이터 포맷과 통신 순서

1. 제어 레지스터로 통신 설정(동기/비동기, 데이터 비트 수, 정지 비트수 등)

2. 보레이트 레지스터로 전송속도 설정

 

 

 

USART를 추가한 KUT-128 회로

- 주의사항 : virtual terminal에서 rx/tx polarity를 inverted로 설정해야한다. normal로 했더니 보드레이트나 클럭을 잘 맞춰도 글자가 깨진다.

- 이걸 normal로 해놓고 rxd, txd를 거꾸로 연결해도 동작이 안된다.

 

USART 송신 하기

- 보레이트 9600bps, 정지비트 1비트, 패리티 x, 비동기 데이터 8비트

- 추가 : 아 그냥 문자열만 주면 줄바꿈이 안되서 뒤에 \r\n을 추가했다.

ref : https://www.avrfreaks.net/forum/uart-newline-n-issue

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

void putch(char);

int main()
{
	char str[] = "This is USART0 control program\r\n";
	char *pstr;
	
	UCSR0A = 0x0;
	UCSR0B = 0b00001000;	//송신 인에이블 txen = 1 ->TxDn이 범용 IO포트가 아닌 송신 단자로 동작
	UCSR0C = 0b00000110;	// 비동기 데이터 8비트 모드	bit2/1 전송 데이터 비트 수 설정 11 -> 데이터 전송 8비트
	//UBRR 보 레이트 레지스터
	UBRR0H =0;				//Xtal = 16hz시 buad = 9600
	UBRR0L = 103;
	
	pstr = str;
	while(1){
		while(*pstr){
			putch(*pstr++);
			_delay_ms(1000);
		}
		pstr = str;
		_delay_ms(1000);
	}
}


void putch(char data){
	/*
	UDRE0 = 1?   UCRS0A의 5번째 비트 UDRE
	USART Data Register Emtry로 비어있으면 1 = 송신 준비 완료 인터럽트 발생
	*/
	while(!(UCSR0A & 0x20)){
		_delay_ms(1000);
	}
	// st -> 0 ~ 7(8개 데이터비트) -> (페리티 설정 x) -> stop bit(현재 1개)
	UDR0 = data;				// USART IO register에 데이터 전송
}

 

 

 

virtual terminal test 하기

 USART 송신 후 송수신 실험을 하려고 했는데, 입력을 해도 제대로 동작이 안됬다.

terminal에 입력한게 재대로 전송이 되고 있는지 확인하기 위해 잠깐 구글링 해봤더니

두 터미널을 연결해서 타이핑하면 반영이 되더라 아래 링크는 그 내용

ref: https://electronics.stackexchange.com/questions/420487/cant-type-on-virtual-terminal-proteus

추가 : 또 보다보니 실제 COM 포트를 프로테우스 시뮬레이션 상으로 연동해주는게 있었다.

기회되면 한번 만져봐도 좋을거같다.

ref : https://www.theengineeringprojects.com/2013/05/how-to-use-virtual-terminal-in-proteus.html

 

 

USART 송수신 테스트

- 지금 실제 kut-128 키트가 아닌 proteus 시뮬레이션 환경에서만 사용하다보니, 예제처럼 제대로 동작되지 않는다.

- 애초에 가상 터미널 1개에다가 송신 모드로 잡아놓고  쓰다보니 그 터미널을 echo type 설정을 해서 쓴다고 USART0로부터 값을 수신을 못하니 원하는데로 동작안하는게 당연했다.

- 그래서 atmega128에서는 USART 2개가 제공되므로 가상 터미널 2개를 놓고, 하나는 타이핑 입력 송신 터미널, 남은 하나는 결과 수신 터미널로 잡고 약간 회로와 코드를 변경해서 구현했더니 잘 동작하더라.

 

 

- 천천히 칠땐 괜찮지만 비동기 통신에다가 패리티 체크도 안하다보니, 타이핑을 빠르게 하면 수신 터미널에서 제대로 전달받지 못한다.

 

 

 

마지막으로 이걸 인터럽트로 바꿔보자

고치다가 찾은건데 USART 하나로 송수신 할수 있더라.

이게 전이중 방식이 가능한걸 잊었다.

----

인터럽트 처리가 잘 안되서 일단 여기까지 하고 해결하면 추가 예정

 

--------

220616-1821

아직 인터럽트 문제는 해결 못했고, 아래 표의 포멧처럼 USART 데이터가 날라가는지 보기위해 한번 실험해보았다.

전송 속도는 9600bps이니까

1bit 전송에 대강 0.1ms 정도 걸리겠다.

아래의 코드 대로 first, second, third 처럼 찍어서 보내게 했다.

 

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>


unsigned char first = 0b00110001;
unsigned char second = 0b00110010;
unsigned char third = 0b10000001;
unsigned char fourth = 0b10100101;

int main()
{
	DDRC = 0xff;
	PORTC = 0xff;
	// 송신 to ocsil
	UCSR0A = 0x0;
	//송신 완료 인터럽트 벡터 인에이블, 송신 인에이블 txen = 1 ->TxDn이 범용 IO포트가 아닌 송신 단자로 동작
	UCSR0B = 0b01001000;
	// 비동기 데이터 8비트 모드	bit2/1 전송 데이터 비트 수 설정 11 -> 데이터 전송 8비트
	UCSR0C = 0b00000110;
	//UBRR 보 레이트 레지스터
	UBRR0H = 0;	
	UBRR0L = 103;

	while(1){
		putch(first);
		_delay_ms(20);
		putch(second);
		_delay_ms(20);
		putch(third);
		_delay_ms(20);
		putch(fourth);
		_delay_ms(1000);
	}
}

void putch(char data){
	while(!(UCSR0A & 0x20));
	UDR0 = data;
}

 

프로테우스에서 제공하는 오실로스코프 크기 조절이 안되서

일단 전체 USART 데이터를 볼수 있도록 스케일을 낮춰놨다.

대강 1bit당 0.1ms 가까이 나온다.

 

 

unsigned char first = 0b00110001;
unsigned char second = 0b00110010;
unsigned char third = 0b10000001;
unsigned char fourth = 0b10100101;

 

first 변수에는 1을 2진화 한 값을 담아 보냈다.

이걸 USART 프레임으로 만들면 데이터는 8비트, 스탑 비트 스타트비트는 각각 1개이니 총 10비트로

9600bps이므로 1개는 0.1ms 이고, 10개이므로 1ms 정도 걸린다.

 

max232에서는 반전된 결과가 나오고 있어서 원래 뒤집히도록 나오는걸 알고 있었지만

왜 데이터 비트자리에 0b00110001를 반전해서 안나오나 싶었는데

위의 프레임 포멧을 자세히 보면 데이터 비트 LSB가 가장 앞에서 나오고 있으므로

00110001이 반대로 10001100으로 나오고 앞뒤로 스타트 스탑 비트가 붙는거였다.

  

 

 

 

나머지 값도 이런식으로  나올테니 넘어가고

원래는 왜 인터럽트 처리가 안되는지 보려고 했는데

지금 와서 보니까 이전에 저장한 프로젝트의 코드까지 현재 코드의 내용들로 덮어씌워져서 다날라갔다.

여기다가 중간 중간에 코드블록 해서 올렸는데, 폴링 방식으로 송수신한 코드도 사라졌다.

 

일단 USART는 여기까지 봤으니 통신은 넘어가고 나머지 하드웨어도 다뤄봐야겠다. 

프로테우스로 ATmega128 개발 키트 KUT-128 구현

- 인터럽트 처리를 위한 스위치, LED, FND 4개 정도만 일단 구현함

기본 블링크 예제

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>


int main()
{
	DDRC = 0xff;
	while(1){
		PORTC = 0xf0;
		_delay_ms(500);
		PORTC = 0x0f;
		_delay_ms(500);
	}
}

 

 

기본 FND 예제

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

int main()
{ 
   char i, val;

   DDRB = 0xff;
   DDRF = 0xf0;
   PORTF = 0b11100000;
   
   while(1){
      val = 0b00000001;
      for(i = 0; i < 7;i++){
	 PORTB = val;
	 _delay_ms(500);
	 val = val << 1;
      }
   }
   
   return 0;
}

 

 

 

 

FND 9999 시뮬레이션 실패?

- FND 4개로 9999찍으려 했지만 _delay_ms를 낮게 설정해서 real time으로 동작되지 않는다.

- 거기다 실제 하드웨어처럼 잔상이 남지 않아 4개의 수가 동시에 나오지 않는다.

 

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

char seg_pat[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
		  0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

void seg4_out(int);
		  
int main()
{ 
	int num = 0;
	
	DDRB = 0xff;
	DDRF = 0b11110000;
	while(1){
		seg4_out(num);
		num++;
		if (num > 9999)
			num = 0;
	}
}

void seg4_out(int num){
	int i, N1000, N100, N10, N1, buf;
	
	N1000 = num / 1000;
	buf = num % 1000;
	N100 = buf / 100;
	buf = buf % 100;
	N10 = buf / 10;
	N1 = buf % 10;
	
	for(i=0;i < 10;i++){
		PORTF = 0b11100000;
		PORTB = seg_pat[N1];
		_delay_ms(10);
		
		PORTF = 0b11010000;
		PORTB = seg_pat[N10];
		_delay_ms(10);
		
		PORTF = 0b10110000;
		PORTB = seg_pat[N100];
		_delay_ms(10);
		
		PORTF = 0b01110000;
		PORTB = seg_pat[N1000];
		_delay_ms(10);
	}
}

 

fnd 2개도 제대로 안된다. 인터럽트랑 같이 쓰고싶었는데

 

 

 

FND, INT 예제

- FND 2개도 못쓰니 그냥 FND 여러개 달린걸 써야할거같긴한데 그러면 원래 KUT-128 보드를 바꾸는거라 어떻게 할지 고민이다.

- 대신 외부 인터럽트로 FND 1개의 숫자 올리는 예제를 구현했다.

- 근데 mega128.h가 아닌 avr/interrupt.h를 사용해서 기존 보던 책과는 조금 다르게 ISR(INT4_ vect)로 인터럽트 정의한다.

- 근데 전역 변수 N1을 선언하고 썻는데 제대로 인터럽트 동작이 안되더라.

 => 아래의 링크를 보니 전역 변수에 volatile을 줬더니 문제없이 동작한다. 찾아보니 최적화 과정에서 지워졌나보다.

https://exploreembedded.com/wiki/AVR_External_Interrupts

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

char seg_pat[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
		  0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

volatile int N1 = 0;
		  
int main()
{
	DDRB = 0xff;
	DDRC = 0xff;
	DDRF = 0b11110000;
	
	EICRB = 0b00000010;
	EIMSK = 0b00010000;
	SREG = 0x80;
	PORTF = 0b11100000;
	
	while(1){
		PORTB = seg_pat[N1];
		PORTC = 0xff;
		_delay_ms(1000);
	}
}

ISR(INT4_vect){
	N1 = (N1 + 1) % 10;
}

 

진도를 더 나가고 싶긴한데, 책을 두고와서 더 나갈수가 없다 ;;

내일 타이머랑 통신으로 넘어가야지

+ Recent posts