AXI_Sensor IP
1. 사용 코드 _ Ultrasonic_DHT11
// ============================================================================
// module: ultrasonic_correction_dht11
// - 초음파(raw) + DHT11(온도) → 보정된 거리(dist_corr)
// - 나눗셈을 순차 서브트랙터(divider)로, 곱셈·나눗셈 파이프라이닝 적용
// - 원 구조와 제어 흐름은 그대로 유지
// ============================================================================
module ultrasonic_correction_dht11 (
input iCLK, // 시스템 클럭 (100MHz 가정)
input iRSTn, // Active-low 리셋
input iEN, // 초음파 측정 트리거(1clk 엣지)
inout ioDATA_DHT11,// DHT11 데이터 라인
input iECHO, // 초음파 Echo 입력
output oTRIG, // 초음파 Trig 출력
output [11:0] oDIST_CORR // 보정된 거리(cm)
);
//==========================================================================
// 1) 내부 신호 선언
//==========================================================================
wire [11:0] dist_raw; // raw 거리(cm)
wire [7:0] temperature; // DHT11 온도(°C)
reg [11:0] dist_corr; // 보정 거리 출력
// 파이프 스테이지 #1: 음속 인자 (0.1m/s 단위)
reg [11:0] factor_reg;
// 파이프 스테이지 #2: 거리×인자 (24비트)
reg [23:0] mult_reg;
// 파이프 스테이지 #3: 순차 뺄셈 나눗셈
reg [23:0] div_reg;
reg [11:0] quot_reg;
reg div_busy;
//==========================================================================
// 2) 출력 매핑
//==========================================================================
assign oDIST_CORR = dist_corr;
//==========================================================================
// 3) 하위 모듈 인스턴스화
//==========================================================================
ultrasonic_sensor u_us (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iEN (iEN),
.iECHO (iECHO),
.oTRIG (oTRIG),
.oDIST (dist_raw),
.oLED_DEBUG()
);
dht11_cntr u_dht (
.iCLK (iCLK),
.iRSTn (iRSTn),
.ioDATA_DHT11(ioDATA_DHT11),
.oHUMI (),
.oTEMP (temperature),
.oLED_DEBUG ()
);
//==========================================================================
// 4) 파이프라인 스테이지 #1: factor = (3310 + 6*T)/10 → 0.1m/s 단위
// 6*T = (T<<2)+(T<<1), 3310는 상수
//==========================================================================
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
factor_reg <= 12'd0;
end else begin
// 온도 기반 음속 인자 계산 (0.1m/s 단위)
// 331.0m/s → 3310(0.1m/s), 0.6*T → 6*T
factor_reg <= 12'd3310
+ (temperature << 2)
+ (temperature << 1);
end
end
//==========================================================================
// 5) 파이프라인 스테이지 #2: raw(cm) × factor(0.1m/s 단위): 시프트+덧셈 누적
//==========================================================================
integer i;
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
mult_reg <= 24'd0;
end else begin
for (i = 0; i < 12; i = i + 1) begin
if (factor_reg[i])
mult_reg = mult_reg + ({12'd0, dist_raw} << i);
end
end
end
//==========================================================================
// 6) 파이프라인 스테이지 #3: 순차적 나눗셈(div_reg ÷ 3430) → quot_reg
// - iEN 펄스가 오면 mult_reg 적재 후 busy 시작
// - 매 클럭 div_reg>=3430 이면 빼고 quot_reg++
// - div_reg<3430 이면 종료하고 결과 저장
//==========================================================================
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
div_reg <= 24'd0;
quot_reg <= 12'd0;
dist_corr <= 12'd0;
div_busy <= 1'b0;
end
else if (iEN) begin
// 새로운 측정 트리거
div_reg <= mult_reg;
quot_reg <= 12'd0;
div_busy <= 1'b1;
end
else if (div_busy) begin
if (div_reg >= 24'd3430) begin
// 빼기 반복
div_reg <= div_reg - 24'd3430;
quot_reg <= quot_reg + 12'd1;
end else begin
// 나눗셈 완료
div_busy <= 1'b0;
dist_corr <= quot_reg;
end
end
end
endmodule
2. 사용코드 _Servo _tilt
//==============================================================================
// module: servo2_tilt_ctrl.v
// - iSET_ANGLE 을 통한 단일 위치 이동 기능 추가
// - FSM: S_IDLE → S_MOVE_SET → S_ADJUST → S_DONE → S_IDLE
//==============================================================================
module servo2_tilt_ctrl (
input iCLK, // 100 MHz 시스템 클럭
input iRSTn, // Active-low 리셋
input iEN, // 자동 보정 트리거(상승 엣지)
input [3:0] iSET_ANGLE, // 임의 이동 스텝(0~14)
input signed [15:0] iPITCH, // MPU6050 피치 값(° 단위)
output oSERVO_2, // 서보2 PWM 출력
output oEN_SERVO_1, // Servo 1 트리거 신호로 재사용
output [7:0] oANGLE2_CUR, // 현재 서보2 각도(°)
output [15:0] oLED_DEBUG // 디버그용(상태 표시)
);
//--------------------------------------------------------------------------
// 1) 파라미터 선언
//--------------------------------------------------------------------------
localparam SYS_CLK_FREQ = 100_000_000; // 100 MHz
localparam MYCLK_FREQ = 2; // 0.5 s 토글
localparam VALUE_COUNT_SEC = SYS_CLK_FREQ/(2*MYCLK_FREQ);
localparam WIDTH_SEC = $clog2(VALUE_COUNT_SEC);
localparam CNT_MIN = 4; // 0° 듀티 (0.44 ms)
localparam MAX_STEP = 14; // 0~14 → 0°~140°, 10° 단위
//--------------------------------------------------------------------------
// 2) 0.5 s 클럭 생성 & 네거티브 엣지 검출
//--------------------------------------------------------------------------
reg [WIDTH_SEC-1:0] rCnt_sec;
reg rClk_sec;
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
rCnt_sec <= 0;
rClk_sec <= 0;
end
else if (rCnt_sec == VALUE_COUNT_SEC-1) begin
rCnt_sec <= 0;
rClk_sec <= ~rClk_sec;
end else
rCnt_sec <= rCnt_sec + 1;
end
wire wClk_sec_neg;
edge_detector u_edge_sec (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iSIG (rClk_sec),
.oEDGE_NEG (wClk_sec_neg)
);
wire wCLK_1M_neg;
clock_div_1ms (
.iCLK(iCLK),
.iRSTn(iRSTn),
.oEDGEn_DIV_1000(wCLK_1M_neg)
);
reg en_servo_1;
assign oEN_SERVO_1 = en_servo_1; // Servo_1 트리거로 재활용
//--------------------------------------------------------------------------
// 3) PWM 서보 IP 인스턴스
//--------------------------------------------------------------------------
reg [3:0] rStep; // 0~14 스텝
wire [7:0] wDuty2 = CNT_MIN + rStep;
pwm_servo_180 u_pwm2 (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iDUTY (wDuty2),
.oPWM (oSERVO_2)
);
//--------------------------------------------------------------------------
// 4) 현재 각도(°) 출력
//--------------------------------------------------------------------------
assign oANGLE2_CUR = rStep * 10;
//--------------------------------------------------------------------------
// 5) prevEN, prevSET 샘플링
//--------------------------------------------------------------------------
reg prevEN;
reg [3:0] prevSET;
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
prevEN <= 1'b0;
prevSET <= 4'd0;
end else begin
prevEN <= iEN;
prevSET <= iSET_ANGLE;
end
end
//--------------------------------------------------------------------------
// 6) FSM 정의
//--------------------------------------------------------------------------
localparam [3:0]
S_IDLE = 4'b0001,
S_MOVE_SET = 4'b0010, // ★ 임의 위치 이동
S_ADJUST = 4'b0100, // 자동 보정
S_DONE = 4'b1000;
reg [3:0] state, next_state;
assign oLED_DEBUG = {en_servo_1, 11'd0, ~state};
// Next-state 로직
always @(*) begin
next_state = state;
case (state)
S_IDLE: begin
if (prevSET != iSET_ANGLE)
next_state = S_MOVE_SET;
else if (!prevEN && iEN)
next_state = S_ADJUST;
end
S_MOVE_SET: begin
// 0.5 s 후 완료
if (wClk_sec_neg)
next_state = S_IDLE;
end
S_ADJUST: begin
// 수평(스텝7) 도달 후 완료
if ((rStep == 7) && wCLK_1M_neg)
next_state = S_DONE;
end
S_DONE: begin
// DONE 후 바로 IDLE 복귀
next_state = S_IDLE;
end
endcase
end
// State 레지스터 + rStep 제어
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
state <= S_IDLE;
rStep <= 4'd7; // 초기 수평(70°)
en_servo_1 <= 0;
end
else begin
state <= next_state;
case (state)
S_IDLE: begin
end
S_MOVE_SET: begin
// ★ iSET_ANGLE 위치로 단일 이동
if (wClk_sec_neg && iSET_ANGLE <= MAX_STEP)
rStep <= iSET_ANGLE;
end
S_ADJUST: begin
// ★ iPITCH 기반 자동 보정
en_servo_1 <= 0;
if (wClk_sec_neg) begin
if (iPITCH > 3 && rStep < MAX_STEP)
rStep <= rStep + 1;
else if (iPITCH < -3 && rStep > 0)
rStep <= rStep - 1;
else
rStep <= 7;
end
end
S_DONE: begin
en_servo_1 <= 1;
end
endcase
end
end
endmodule
3. 사용 코드_Servo1_PWM
module servo_pwm_top (
input iCLK, // 100MHz 시스템 클럭
input iRSTn, // Active-low 리셋
input iEN, // ★ 스윕 트리거 (상승 엣지)
input [4:0] iSET_ANGLE, // ★ 설정 각도 인덱스(0~18)
output oEN_SONIC, // 거리 센서 트리거 신호로 재사용
output oSERVO, // 서보 PWM 출력
output [4:0] oANGLE_CUR, // 현재 각도(°)
output [15:0] oLED_DEBUG
);
//======================================================================
// 1) 파라미터 선언
//======================================================================
localparam SYS_CLK_FREQ = 100_000_000;
localparam MYCLK_FREQ = 2; // 0.5s 토글
localparam VALUE_COUNT_SEC= SYS_CLK_FREQ/(2*MYCLK_FREQ);
localparam WIDTH_SEC = $clog2(VALUE_COUNT_SEC);
localparam CNT_MIN = 4; // 0도 대응 (0.44ms)
localparam CNT_MAX = 22; // 180도 대응 (2.44ms)
localparam MAX_STEP = 18; // 0~18 → 0~180° (10° 단위)
//======================================================================
// 2) 500ms 타이밍 생성 & 네거티브 엣지 검출
//======================================================================
reg [WIDTH_SEC-1:0] rCnt_sec;
reg rClk_sec;
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
rCnt_sec <= 0;
rClk_sec <= 0;
end else if (rCnt_sec == VALUE_COUNT_SEC-1) begin
rCnt_sec <= 0;
rClk_sec <= ~rClk_sec;
end else
rCnt_sec <= rCnt_sec + 1;
end
wire wClk_sec_neg;
edge_detector UED (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iSIG (rClk_sec),
.oEDGE_NEG(wClk_sec_neg)
);
assign oEN_SONIC = wClk_sec_neg; // 거리 센서 트리거로 재활용
//======================================================================
// 3) PWM 서보 드라이버 인스턴스
//======================================================================
// ★ FSM 출력 rDUTY (0~18)에 CNT_MIN 더해서 실제 듀티 생성
reg [7:0] rDUTY;
wire [4:0] wPWM_DUTY = CNT_MIN + rDUTY;
pwm_servo_180 U_SERVO (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iDUTY (wPWM_DUTY),
.oPWM (oSERVO)
);
//======================================================================
// 4) 현재 각도 & FND 표시
//======================================================================
// 10도 단위 → 실제 각도(°)
assign oANGLE_CUR = rDUTY;
//======================================================================
// 5) FSM: IDLE → MOVE_SET → SWEEP → DONE → IDLE
//======================================================================
// ★ 상승 엣지/값 변경 감지용 레지스터
reg prevEN;
reg [4:0] prevSET;
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
prevEN <= 1'b0;
prevSET <= 5'd0;
end else begin
prevEN <= iEN;
prevSET <= iSET_ANGLE;
end
end
// 상태 정의
localparam [3:0]
S_IDLE = 4'b0001,
S_MOVE_SET = 4'b0010,
S_SWEEP = 4'b0100,
S_DONE = 4'b1000;
reg [3:0] state, next_state;
reg [4:0] rStep; // 스윕 카운터 (0~18)
assign oLED_DEBUG = ~state;
// Next-State 로직
always @(*) begin
next_state = state;
case (state)
S_IDLE:
// SET 값이 바뀌면 단일 이동
if (prevSET != iSET_ANGLE)
next_state = S_MOVE_SET;
// iEN 상승 엣지 시 전체 스윕
else if (!prevEN && iEN)
next_state = S_SWEEP;
S_MOVE_SET:
// 1회 이동 후 바로 IDLE 복귀
if (wClk_sec_neg)
next_state = S_IDLE;
S_SWEEP:
// 스텝 끝나면 DONE
if (wClk_sec_neg && rStep == MAX_STEP)
next_state = S_DONE;
S_DONE:
// DONE 후 IDLE
next_state = S_IDLE;
endcase
end
// State 레지스터 + 출력 제어
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
state <= S_IDLE;
rDUTY <= 8'd0;
rStep <= 5'd0;
end else begin
state <= next_state;
case (state)
S_IDLE: begin
// 스윕 시작 시 스텝 카운터 리셋
if (!prevEN && iEN)
rStep <= 5'd0;
end
S_MOVE_SET: begin
// 단일 위치 이동: wClk_sec_neg 타이밍에 iSET_ANGLE 적용
if (wClk_sec_neg)
rDUTY <= iSET_ANGLE;
end
S_SWEEP: begin
// 전체 스윕: wClk_sec_neg마다 rStep→rDUTY
if (wClk_sec_neg) begin
rDUTY <= rStep;
rStep <= rStep + 1;
end
end
S_DONE: begin
rDUTY <= 9;
end
endcase
end
end
endmodule
4. 사용 코드 _좌표 변환
module cartesian_transform (
input clk, // 시스템 클럭
input rstn, // Active-low reset
input [4:0] angle_idx, // 0→0°, 1→10° … 18→180°
input [11:0] dist_in, // 보정된 거리(cm)
output reg oREADY,
output reg signed [11:0] x_out, // 변환된 X (cm)
output reg signed [11:0] y_out // 변환된 Y (cm)
);
// 고정소수점 폭
localparam FP_BITS = 16;
// 정수×고정소수점 곱셈 결과 폭
wire signed [12+FP_BITS-1:0] mult_x;
wire signed [12+FP_BITS-1:0] mult_y;
// ★ sin/cos 값을 직접 할당할 레지스터
reg signed [FP_BITS-1:0] sin_val; // Q1.15
reg signed [FP_BITS-1:0] cos_val; // Q1.15
// ① angle_idx 혹은 dist_in 이 바뀌면 새로운 좌표를 계산하고,
// 그 계산값이 register에 승격되는 클럭 타이밍에 coord_valid 를 1로.
wire new_input = (angle_idx != prev_angle_idx) || (dist_in != prev_dist_in);
// ② prev_angle_idx, prev_dist_in 저장
reg [4:0] prev_angle_idx;
reg [11:0] prev_dist_in;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
prev_angle_idx <= 0;
prev_dist_in <= 0;
end else begin
prev_angle_idx <= angle_idx;
prev_dist_in <= dist_in;
end
end
// ③ coord_valid 펄스 생성
reg coord_valid;
always @(posedge clk or negedge rstn) begin
if (!rstn) coord_valid <= 1'b0;
else if (new_input) coord_valid <= 1'b1;
else coord_valid <= 1'b0;
end
// ④ 바로 위 방법대로 oCOORD_RDY 펄스 만들기
reg prev_coord_valid;
always @(posedge clk or negedge rstn) begin
if (!rstn) prev_coord_valid <= 1'b0;
else prev_coord_valid <= coord_valid;
end
always @(posedge clk or negedge rstn) begin
if (!rstn) oREADY <= 1'b0;
else oREADY <= coord_valid & ~prev_coord_valid;
end
//--------------------------------------------------------------------------
// 1) combinational LUT: angle_idx → sin_val, cos_val
//--------------------------------------------------------------------------
always @* begin
case(angle_idx)
// -- sin 테이블 (Q1.15)
5'd0 : sin_val = 16'h0000;
5'd1 : sin_val = 16'h163A;
5'd2 : sin_val = 16'h2BC7;
5'd3 : sin_val = 16'h4000;
5'd4 : sin_val = 16'h5247;
5'd5 : sin_val = 16'h620E;
5'd6 : sin_val = 16'h6EDA;
5'd7 : sin_val = 16'h7848;
5'd8 : sin_val = 16'h7E0E;
5'd9 : sin_val = 16'h8000;
5'd10 : sin_val = 16'h7E0E;
5'd11 : sin_val = 16'h7848;
5'd12 : sin_val = 16'h6EDA;
5'd13 : sin_val = 16'h620E;
5'd14 : sin_val = 16'h5247;
5'd15 : sin_val = 16'h4000;
5'd16 : sin_val = 16'h2BC7;
5'd17 : sin_val = 16'h163A;
5'd18 : sin_val = 16'h0000;
default: sin_val = 16'h0000;
endcase
end
always @* begin
case(angle_idx)
// -- cos 테이블 (Q1.15)
5'd0 : cos_val = 16'h8000;
5'd1 : cos_val = 16'h7E0E;
5'd2 : cos_val = 16'h7848;
5'd3 : cos_val = 16'h6EDA;
5'd4 : cos_val = 16'h620E;
5'd5 : cos_val = 16'h5247;
5'd6 : cos_val = 16'h4000;
5'd7 : cos_val = 16'h2BC7;
5'd8 : cos_val = 16'h163A;
5'd9 : cos_val = 16'h0000;
5'd10 : cos_val = 16'hE9C6;
5'd11 : cos_val = 16'hD439;
5'd12 : cos_val = 16'hC000;
5'd13 : cos_val = 16'hADB9;
5'd14 : cos_val = 16'h9DF2;
5'd15 : cos_val = 16'h9126;
5'd16 : cos_val = 16'h87B8;
5'd17 : cos_val = 16'h81F2;
5'd18 : cos_val = 16'h8000;
default: cos_val = 16'h8000;
endcase
end
//--------------------------------------------------------------------------
// 2) 곱셈
// dist_in: unsigned[11:0], sin_val/cos_val: signed[15:0]
// → signed[27:0] 결과
//--------------------------------------------------------------------------
assign mult_x = $signed({1'b0, dist_in}) * sin_val;
assign mult_y = $signed({1'b0, dist_in}) * cos_val;
//--------------------------------------------------------------------------
// 3) Q1.15 → 정수(cm) 변환: 위 곱셈 결과의 상위 비트 취함
// mult_x[27:15] → signed[12:0] → x_out[11:0]으로 트렁케이트
//--------------------------------------------------------------------------
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
x_out <= 0;
y_out <= 0;
end else begin
x_out <= mult_x[27:16]; // (11+16-1 down to 16)
y_out <= mult_y[27:16];
end
end
endmodule
AXI_VIHICLE_IP
1. 사용 코드 _DCMotor
// ============================================================================
// motor_autonomous_ctrl.v
// - one-hot 5상태 FSM
// - ST_IDLE → ST_FWD(지속) → ST_LEFT/RIGHT(0.245s) → ST_STOP(1clk) → ST_IDLE
// ============================================================================
module motor_autonomous_ctrl (
input iCLK, // 100 MHz 시스템 클럭
input iRSTn, // Active-low 리셋
input iEN_START, // 전진 버튼 (펄스)
input iEN_STOP, // 정지 버튼 (펄스)
input iEN_LEFT, // 좌회전 버튼 (펄스)
input iEN_RIGHT, // 우회전 버튼 (펄스)
output oMOTOR_A1, // 왼쪽 모터 전진
output oMOTOR_A2, // 왼쪽 모터 후진 (사용 안 함)
output oMOTOR_B1, // 오른쪽 모터 전진
output oMOTOR_B2, // 오른쪽 모터 후진 (사용 안 함)
output [15:0] oLED_DEBUG
);
//--------------------------------------------------------------------------
// 1) 상태(one-hot 5비트) 정의
//--------------------------------------------------------------------------
localparam [4:0]
S_IDLE = 5'b00001, // 대기
S_FWD = 5'b00010, // 전진(지속)
S_STOP = 5'b00100, // 정지(1clk)
S_LEFT = 5'b01000, // 좌회전(0.245s)
S_RIGHT = 5'b10000; // 우회전(0.245s)
//--------------------------------------------------------------------------
// 2) 0.245 s 타이머 (100 MHz → 24_500_000 클럭)
//--------------------------------------------------------------------------
localparam ROTATE_TIME = 24_500_000; // 10도 회전
localparam WIDTH_RT = $clog2(ROTATE_TIME);
//--------------------------------------------------------------------------
// 3) 내부 신호
//--------------------------------------------------------------------------
wire wStartEdge, wStopEdge, wLeftEdge, wRightEdge;
reg [4:0] rState, rNext;
reg [WIDTH_RT-1:0] rTimerCnt;
//--------------------------------------------------------------------------
// 4) 버튼 엣지 검출 인스턴스
//--------------------------------------------------------------------------
edge_detector uED_START (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iSIG (iEN_START),
.oEDGE_NEG (wStartEdge)
);
edge_detector uED_STOP (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iSIG (iEN_STOP),
.oEDGE_NEG (wStopEdge)
);
edge_detector uED_LEFT (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iSIG (iEN_LEFT),
.oEDGE_NEG (wLeftEdge)
);
edge_detector uED_RIGHT (
.iCLK (iCLK),
.iRSTn (iRSTn),
.iSIG (iEN_RIGHT),
.oEDGE_NEG (wRightEdge)
);
//--------------------------------------------------------------------------
// 5) FSM Next-State 로직 (combinational)
//--------------------------------------------------------------------------
always @(*) begin
rNext = rState;
case (rState)
// IDLE: START만 받아서 전진으로
S_IDLE: begin
if (wStartEdge) rNext = S_FWD;
else rNext = S_IDLE;
end
// FWD: STOP/LEFT/RIGHT 에만 반응, 아니면 계속 전진
S_FWD: begin
if (wStopEdge) rNext = S_STOP;
else if (wLeftEdge) rNext = S_LEFT;
else if (wRightEdge) rNext = S_RIGHT;
else rNext = S_FWD;
end
// STOP: 1clk 만 유지 후 IDLE
S_STOP: rNext = S_IDLE;
// LEFT: 0.245s 타이머 끝나면 IDLE, 아니면 계속 LEFT
S_LEFT: begin
if (rTimerCnt >= ROTATE_TIME) rNext = S_IDLE;
else rNext = S_LEFT;
end
// RIGHT: 0.245s 타이머 끝나면 IDLE, 아니면 계속 RIGHT
S_RIGHT: begin
if (rTimerCnt >= ROTATE_TIME) rNext = S_IDLE;
else rNext = S_RIGHT;
end
// 기본 복귀
default: rNext = S_IDLE;
endcase
end
//--------------------------------------------------------------------------
// 6) FSM 상태 & 타이머 업데이트 (sequential)
//--------------------------------------------------------------------------
always @(posedge iCLK or negedge iRSTn) begin
if (!iRSTn) begin
rState <= S_IDLE;
rTimerCnt <= 0;
end else begin
rState <= rNext;
// 좌/우 회전 중일 때만 카운터 ↑, entry 시 리셋
if (rState == S_LEFT || rState == S_RIGHT) begin
if (rState != rNext)
rTimerCnt <= 0;
else
rTimerCnt <= rTimerCnt + 1;
end
else
rTimerCnt <= 0;
end
end
//--------------------------------------------------------------------------
// 7) 모터 드라이브 (combinational)
//--------------------------------------------------------------------------
// 전진: A1,B1 = 1
// 정지/IDLE: 모두 0
// 좌회전: 왼쪽 A1만 =1
// 우회전: 오른쪽 B1만 =1
assign oMOTOR_A1 = (rState == S_FWD) || (rState == S_LEFT);
assign oMOTOR_A2 = 0;
assign oMOTOR_B1 = (rState == S_FWD) || (rState == S_RIGHT);
assign oMOTOR_B2 = 0;
//--------------------------------------------------------------------------
// 8) 상태 디버그 출력
//--------------------------------------------------------------------------
assign oLED_DEBUG[4:0] = ~rState;
endmodule
VITIS_I2C_Control
1. 사용코드 _I2c 3 Sensor (MPU6050, TXT LCD, OLED)
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xiic.h"
#include "sleep.h"
#include "font8x8_basic.h"
//------------------------------------------------------------------------------
// I²C 마스터 & 채널
//------------------------------------------------------------------------------
static XIic iic_inst;
#define IIC_CHNL XPAR_AXI_IIC_0_BASEADDR
//------------------------------------------------------------------------------
// 장치 7-bit 주소
//------------------------------------------------------------------------------
#define LCD_ADDR 0x3F // Text LCD (PCF8574 기반)
#define OLED_ADDR 0x3C // SSD1306 OLED
#define MPU_ADDR 0x68 // MPU6050
//------------------------------------------------------------------------------
// MPU6050 레지스터
//------------------------------------------------------------------------------
#define REG_PWR_MGMT1 0x6B
#define REG_SMPLRT_DIV 0x19
#define REG_CONFIG 0x1A
#define REG_GYRO_CONFIG 0x1B
#define REG_ACCEL_CONFIG 0x1C
#define REG_ACCEL_X_H 0x3B
//------------------------------------------------------------------------------
// OLED Control 바이트
//------------------------------------------------------------------------------
#define OLED_CTL_CMD 0x00
#define OLED_CTL_DAT 0x40
//------------------------------------------------------------------------------
// ASCII 폰트 (0x20~0x7F) extern 선언
// 반드시 font8x8_basic.h 를 프로젝트에 포함해야 합니다.
extern const uint8_t font8x8_basic[96][8];
//==============================================================================
// 전역 버퍼: OLED GDDRAM 에 대응 (페이지 0~7 × 컬럼 0~127)
//==============================================================================
static uint8_t oled_buf[8][128];
//------------------------------------------------------------------------------
// mpu 함수 선언
//------------------------------------------------------------------------------
void mpu_write_reg(uint8_t reg, uint8_t val);
void mpu_read_regs(uint8_t start_reg, uint8_t *dst, int len);
static int16_t compute_mpu_pitch(int16_t ax,int16_t ay,int16_t az);
//------------------------------------------------------------------------------
// lcd 함수 선언
//------------------------------------------------------------------------------
void LCD_WriteCommand(uint8_t cmd);
void LCD_WriteData(uint8_t d);
void LCD_Init(void);
void LCD_GotoXY(uint8_t row, uint8_t col);
void LCD_WriteSignedInt(int16_t v);
//------------------------------------------------------------------------------
// oled 함수 선언
//------------------------------------------------------------------------------
void oled_write_cmd(uint8_t cmd);
void oled_write_data(uint8_t d);
void oled_init(void);
void oled_clear(void);
void oled_puts_str(int page, int col, const char *s);
//------------------------------------------------------------------------------
// oled 픽셀 그리기 & 버퍼 업데이트 함수 선언
//------------------------------------------------------------------------------
void oled_update_column(int page, int col);
void oled_draw_pixel(int x, int y);
//==============================================================================
// main: 초기화 → 루프(읽기→UART/LCD/OLED 출력)
//==============================================================================
int main(void) {
uint8_t buf[14];
int16_t ax, ay, az, gx, gy, gz;
init_platform();
xil_printf("\n\r=== I2C 3-Sensor Integrated test ===\n\r");
// 1) I²C 마스터 초기화·시작
XIic_Initialize(&iic_inst, IIC_CHNL);
XIic_Start (&iic_inst);
msleep(10);
// 2) LCD 초기화
LCD_Init();
msleep(10);
// 3) OLED 초기화·클리어
oled_init();
oled_clear();
msleep(10);
// 4) MPU6050 슬립 해제·감도 설정
mpu_write_reg(REG_PWR_MGMT1, 0x00);
mpu_write_reg(REG_SMPLRT_DIV, 0x07);
mpu_write_reg(REG_CONFIG, 0x06);
mpu_write_reg(REG_GYRO_CONFIG, 0x18);
mpu_write_reg(REG_ACCEL_CONFIG, 0x10);
msleep(10);
// 5) 메인 루프: 250 ms 간격으로 읽고 출력
while (1) {
// — MPU6050 6축 읽기 —
mpu_read_regs(REG_ACCEL_X_H, buf, 14);
ax = (buf[0]<<8)|buf[1];
ay = (buf[2]<<8)|buf[3];
az = (buf[4]<<8)|buf[5];
gx = (buf[8]<<8)|buf[9];
gy = (buf[10]<<8)|buf[11];
gz = (buf[12]<<8)|buf[13];
int16_t pitch = compute_mpu_pitch(ax, ay, az);
// — UART 출력 —
xil_printf("AX=%6d AY=%6d AZ=%6d pitch =%4d\n\r", ax, ay, az, pitch);
// — LCD: 2행에 AX~GZ 한 줄씩 —
LCD_GotoXY(0,0);
LCD_WriteSignedInt(ax);
LCD_WriteSignedInt(ay);
LCD_WriteSignedInt(az);
LCD_GotoXY(1,0);
LCD_WriteSignedInt(gx);
LCD_WriteSignedInt(gy);
LCD_WriteSignedInt(gz);
// — OLED: 페이지0~5 에 AX~GZ 한 줄씩 출력 —
char tmp[7];
// 6자리 문자열: 부호+4숫자 + 널
sprintf(tmp, "%+5d", ax);
oled_puts_str(0, 0, tmp);
sprintf(tmp, "%+5d", ay);
oled_puts_str(1, 0, tmp);
sprintf(tmp, "%+5d", az);
oled_puts_str(2, 0, tmp);
sprintf(tmp, "%+5d", gx);
oled_puts_str(3, 0, tmp);
sprintf(tmp, "%+5d", gy);
oled_puts_str(4, 0, tmp);
sprintf(tmp, "%+5d", gz);
oled_puts_str(5, 0, tmp);
sleep(1);
// — (x=64,y=32)에 점 하나 찍기 —
oled_draw_pixel(0, 0);
sleep(1);
oled_draw_pixel(120, 0);
sleep(1);
oled_draw_pixel(120, 60);
sleep(1);
oled_draw_pixel(0, 60);
sleep(1);
oled_draw_pixel(60, 30);
sleep(1);
}
cleanup_platform();
return 0;
}
//------------------------------------------------------------------------------
// MPU6050 I²C 쓰기 헬퍼
//------------------------------------------------------------------------------
void mpu_write_reg(uint8_t reg, uint8_t val) {
uint8_t buf[2] = { reg, val };
XIic_Send(iic_inst.BaseAddress, MPU_ADDR, buf, 2, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
//------------------------------------------------------------------------------
// MPU6050 I²C 연속 읽기 헬퍼
//------------------------------------------------------------------------------
void mpu_read_regs(uint8_t start_reg, uint8_t *dst, int len) {
// 1) 읽을 레지스터 주소 전송 (Repeated Start 유지)
XIic_Send(iic_inst.BaseAddress, MPU_ADDR, &start_reg, 1, 0);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
// 2) 바로 읽기 → STOP
XIic_Recv(iic_inst.BaseAddress, MPU_ADDR, dst, len, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
//------------------------------------------------------------------------------
// @brief 근사: raw→pitch(°) 정수
// sqrt≈max+0.5·min, atan2≈x/(1+0.28x²)
//------------------------------------------------------------------------------
static int16_t compute_mpu_pitch(int16_t ax,int16_t ay,int16_t az) {
const float G=4096.0f;
float x=(float)ax/G, y=(float)ay/G, z=(float)az/G;
float ay0=y<0?-y:y, az0=z<0?-z:z;
float vmax= ay0>az0? ay0:az0;
float vmin= ay0>az0? az0:ay0;
float denom = vmax + 0.5f*vmin + 1e-6f;
float r = x/denom;
float rad = r/(1.0f + 0.28f*r*r);
float deg = rad * 57.2957795f;
return (int16_t)(deg + (deg>=0?0.5f:-0.5f));
}
//------------------------------------------------------------------------------
// Text LCD 제어 함수들 (기존 코드 그대로)
//------------------------------------------------------------------------------
void LCD_WriteCommand(uint8_t cmd) {
uint8_t high = cmd & 0xF0;
uint8_t low = (cmd<<4)& 0xF0;
uint8_t seq[4] = {
high|0x0C, high|0x08,
low |0x0C, low |0x08
};
XIic_Send(iic_inst.BaseAddress, LCD_ADDR, seq, 4, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
void LCD_WriteData(uint8_t d) {
uint8_t high = d & 0xF0;
uint8_t low = (d <<4)& 0xF0;
uint8_t seq[4] = {
high|0x0D, high|0x08,
low |0x0D, low |0x08
};
XIic_Send(iic_inst.BaseAddress, LCD_ADDR, seq, 4, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
void LCD_Init(void) {
msleep(50);
LCD_WriteCommand(0x33); msleep(5);
LCD_WriteCommand(0x32);
LCD_WriteCommand(0x28);
LCD_WriteCommand(0x0F);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01); msleep(5);
}
void LCD_GotoXY(uint8_t row, uint8_t col) {
uint8_t addr = 0x80 | (row<<6) | col;
LCD_WriteCommand(addr);
}
// 부호 포함 5자리 찍기
void LCD_WriteSignedInt(int16_t v) {
char tmp[6];
// "%+5d" → 항상 부호, 총 5칸
sprintf(tmp, "%+5d", v);
for (int i = 0; i < 5; i++) {
LCD_WriteData((uint8_t)tmp[i]);
}
}
//------------------------------------------------------------------------------
// OLED 제어 함수들
//------------------------------------------------------------------------------
// Control + 명령어 1바이트
void oled_write_cmd(uint8_t cmd) {
uint8_t buf[2] = { OLED_CTL_CMD, cmd };
XIic_Send(iic_inst.BaseAddress, OLED_ADDR, buf, 2, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
// Control + 데이터 1바이트
void oled_write_data(uint8_t d) {
uint8_t buf[2] = { OLED_CTL_DAT, d };
XIic_Send(iic_inst.BaseAddress, OLED_ADDR, buf, 2, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
// SSD1306 초기화 (페이지 주소 모드 포함)
void oled_init(void) {
const uint8_t seq[] = {
0xAE, // display off
0x20,0x02, // page addressing mode
0xB0, // page0
0x00,0x10, // col addr = 0
0x40, // start line = 0
0x81,0x7F, // contrast
0xA1, // seg remap
0xC8, // com scan dec
0xA6, // normal display
0xA8,0x3F, // multiplex 1/64
0xD3,0x00, // display offset
0xD5,0x80, // clk div
0xD9,0x22, // pre-charge
0xDA,0x12, // com pins
0xDB,0x20, // vcomh
0x8D,0x14, // charge pump on
0xAF // display on
};
for (int i = 0; i < (int)sizeof(seq); i++) {
oled_write_cmd(seq[i]);
}
}
// 전체 화면 클리어
void oled_clear(void) {
for (int p = 0; p < 8; p++) {
oled_write_cmd(0xB0 | p);
oled_write_cmd(0x00);
oled_write_cmd(0x10);
for (int c = 0; c < 128; c++) {
oled_write_data(0x00);
}
}
}
//------------------------------------------------------------------------------
// 문자열(부호 포함 숫자) 출력 helper
//------------------------------------------------------------------------------
// page: 0~7, col: 문자 단위(0~15), s: null-terminated 문자열
void oled_puts_str(int page, int col, const char *s) {
// 1) 페이지·컬럼 설정
oled_write_cmd(0xB0 | page);
uint8_t x = col * 8;
oled_write_cmd(0x00 | (x & 0x0F));
oled_write_cmd(0x10 | ((x >> 4) & 0x0F));
// 2) 글자별 8바이트 폰트 전송
while (*s) {
uint8_t buf[9];
buf[0] = OLED_CTL_DAT;
// ASCII 0x20~0x7F 대응
memcpy(buf+1, font8x8_basic[(uint8_t)*s - 0x20], 8);
XIic_Send(iic_inst.BaseAddress, OLED_ADDR, buf, 9, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
s++;
}
}
//------------------------------------------------------------------------------
// OLED 픽셀 업데이트: 버퍼→실제 GDDRAM (한 컬럼만 갱신)
//------------------------------------------------------------------------------
void oled_update_column(int page, int col) {
oled_write_cmd(0xB0 | page); // page 설정
oled_write_cmd(0x00 | (col & 0x0F)); // col low
oled_write_cmd(0x10 | ((col>>4)&0x0F)); // col high
oled_write_data(oled_buf[page][col]); // 버퍼값 전송
}
//------------------------------------------------------------------------------
// (x,y) 에 1픽셀 그리기 (버퍼 갱신 + 해당 컬럼만 갱신)
//------------------------------------------------------------------------------
void oled_draw_pixel(int x, int y) {
if (x<0||x>127||y<0||y>63) return;
int page = y / 8;
int bit = y % 8;
oled_buf[page][x] |= (1<<bit); // 버퍼에 비트 세팅
oled_update_column(page, x); // 실제 화면에 바로 반영
}
Final System Control ( Vitis C )
1. 사용 코드_ Final C 동작제어 코드
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xiic.h"
#include "sleep.h"
#include "font8x8_basic.h"
#include <math.h>
#include <stdlib.h>
//------------------------------------------------------------------------------
// I²C 마스터 & 채널
//------------------------------------------------------------------------------
XIic iic_inst;
#define IIC_CHNL XPAR_AXI_IIC_0_BASEADDR
//------------------------------------------------------------------------------
// AXI-Sensor-Hub MMIO 베이스 주소 & 레지스터 오프셋
//------------------------------------------------------------------------------
#define HUB_BASE XPAR_AXI_SENSOR_HUB_0_BASEADDR
//------------------------------------------------------------------------------
// 장치 7-bit 주소
//------------------------------------------------------------------------------
#define LCD_ADDR 0x3F // Text LCD (PCF8574 기반)
#define OLED_ADDR 0x3C // SSD1306 OLED
#define MPU_ADDR 0x68 // MPU6050
//------------------------------------------------------------------------------
// MPU6050 레지스터
//------------------------------------------------------------------------------
#define REG_PWR_MGMT1 0x6B
#define REG_SMPLRT_DIV 0x19
#define REG_CONFIG 0x1A
#define REG_GYRO_CONFIG 0x1B
#define REG_ACCEL_CONFIG 0x1C
#define REG_ACCEL_X_H 0x3B
//------------------------------------------------------------------------------
// OLED Control 바이트
//------------------------------------------------------------------------------
#define OLED_CTL_CMD 0x00
#define OLED_CTL_DAT 0x40
//------------------------------------------------------------------------------
// ASCII 폰트 (0x20~0x7F) extern 선언
// 반드시 font8x8_basic.h 를 프로젝트에 포함해야 합니다.
extern const uint8_t font8x8_basic[96][8];
//==============================================================================
// 전역 버퍼: OLED GDDRAM 에 대응 (페이지 0~7 × 컬럼 0~127)
//==============================================================================
static uint8_t oled_buf[8][128];
//------------------------------------------------------------------------------
// mpu 함수 선언
//------------------------------------------------------------------------------
void mpu_write_reg(uint8_t reg, uint8_t val);
void mpu_read_regs(uint8_t start_reg, uint8_t *dst, int len);
static int16_t compute_mpu_pitch(int16_t ax,int16_t ay,int16_t az);
//------------------------------------------------------------------------------
// lcd 함수 선언
//------------------------------------------------------------------------------
void LCD_WriteCommand(uint8_t cmd);
void LCD_WriteData(uint8_t d);
void LCD_Init(void);
void LCD_GotoXY(uint8_t row, uint8_t col);
void LCD_WriteSignedInt(int16_t v);
//------------------------------------------------------------------------------
// oled 함수 선언
//------------------------------------------------------------------------------
void oled_write_cmd(uint8_t cmd);
void oled_write_data(uint8_t d);
void oled_init(void);
void oled_clear(void);
void oled_puts_str(int page, int col, const char *s);
//------------------------------------------------------------------------------
// oled 픽셀 그리기 & 버퍼 업데이트 함수 선언
//------------------------------------------------------------------------------
void oled_update_column(int page, int col);
void oled_draw_pixel(int x, int y);
//==============================================================================
// main: 초기화 → 2D 매핑 → 최적 각도로 서보1 회전
//==============================================================================
int main(void) {
uint8_t buf[14];
int16_t ax, ay, az;
init_platform();
xil_printf("\n\r=== I2C Stabilization & Avoidance Test ===\n\r");
volatile unsigned int *hub_reg = (volatile unsigned int *)HUB_BASE;
//hub_reg[0] -> set_angle_1(wd)
//hub_reg[1] -> set_angle_2(wd)
//hub_reg[2] -> en_servo_2(wd)
//hub_reg[3] -> mpu_pitch(wd)
//hub_reg[4] -> x_out(rd)
//hub_reg[5] -> y_out(rd)
//hub_reg[6] -> ready(rd)
// 1) I²C 마스터 초기화·시작
XIic_Initialize(&iic_inst, IIC_CHNL);
XIic_Start (&iic_inst);
// msleep(10);
// 2) LCD 초기화
LCD_Init();
// msleep(10);
// 3) OLED 초기화·클리어
oled_init();
oled_clear();
msleep(10);
// 4) MPU6050 슬립 해제·감도 설정
mpu_write_reg(REG_PWR_MGMT1, 0x00);
mpu_write_reg(REG_SMPLRT_DIV, 0x07);
mpu_write_reg(REG_CONFIG, 0x06);
mpu_write_reg(REG_GYRO_CONFIG, 0x18);
mpu_write_reg(REG_ACCEL_CONFIG, 0x10);
// msleep(10);
// 5) 메인 루프: 250 ms 간격으로 읽고 출력
while (1) {
hub_reg[0] = 14;
hub_reg[1] = 3;
hub_reg[2] = 0;
hub_reg[3] = 0;
const int NSTEP = 19; // 0~18 인덱스
int x_arr[NSTEP], y_arr[NSTEP];
hub_reg[0] = 9;
msleep(100);
hub_reg[1] = 10;
msleep(100);
// — MPU6050 6축 읽기 —
mpu_read_regs(REG_ACCEL_X_H, buf, 14);
ax = (buf[0]<<8)|buf[1];
ay = (buf[2]<<8)|buf[3];
az = (buf[4]<<8)|buf[5];
// gx = (buf[8]<<8)|buf[9];
// gy = (buf[10]<<8)|buf[11];
// gz = (buf[12]<<8)|buf[13];
// “틸트(pitch)” 계산
int16_t mpu_pitch = compute_mpu_pitch(ax, ay, az);
// — UART 출력 —
xil_printf("AX =%6d AY =%6d AZ =%6d mpu_pitch =%4d\n\r", ax, ay, az, mpu_pitch);
// msleep(100);
LCD_GotoXY(0,0);
LCD_WriteSignedInt(ax);
LCD_WriteSignedInt(ay);
LCD_WriteSignedInt(az);
print("1");
hub_reg[2] = 1;
usleep(1);
print("2");
msleep(10);
hub_reg[3] = mpu_pitch;
print("3");
oled_clear();
print("4");
for (int idx = 0; idx < NSTEP; idx++) {
// 1) trigger servomove/setangle 은 이미 밖에서 함
// 2) hub_reg[6] 이 1 될 때까지 기다린다
xil_printf("idx = %d", idx);
while ((hub_reg[6] & 0x1) == 0) {
usleep(1);
}
// 3) 1 이 올라왔으면 단 한 번만 읽고
x_arr[idx] = (int)hub_reg[4];
y_arr[idx] = (int)hub_reg[5];
oled_draw_pixel(x_arr[idx], y_arr[idx]);
// 4) flag clear
hub_reg[6] = 0;
}
print("1");
print("20");
msleep(100);
// 최장 거리 좌표 찾기
int sel_idx = 0;
int max_dsq = 0;
for (int idx = 0; idx < NSTEP; ++idx) {
int dsq = x_arr[idx]*x_arr[idx] + y_arr[idx]*y_arr[idx];
if (dsq > max_dsq) {
max_dsq = dsq;
sel_idx = idx;
}
}
// 최장 거리 쪽으로 서보1 회전
hub_reg[0] = sel_idx;
msleep(500);
// LCD 에 선택 각도(°) 표시
LCD_GotoXY(1, 0);
LCD_WriteSignedInt(sel_idx * 10); // 인덱스→° (10° 단위)
msleep(500);
}
cleanup_platform();
return 0;
}
//------------------------------------------------------------------------------
// MPU6050 I²C 쓰기 헬퍼
//------------------------------------------------------------------------------
void mpu_write_reg(uint8_t reg, uint8_t val) {
uint8_t buf[2] = { reg, val };
XIic_Send(iic_inst.BaseAddress, MPU_ADDR, buf, 2, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
//------------------------------------------------------------------------------
// MPU6050 I²C 연속 읽기 헬퍼
//------------------------------------------------------------------------------
void mpu_read_regs(uint8_t start_reg, uint8_t *dst, int len) {
// 1) 읽을 레지스터 주소 전송 (Repeated Start 유지)
XIic_Send(iic_inst.BaseAddress, MPU_ADDR, &start_reg, 1, 0);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
// 2) 바로 읽기 → STOP
XIic_Recv(iic_inst.BaseAddress, MPU_ADDR, dst, len, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
//------------------------------------------------------------------------------
// @brief 근사: raw→pitch(°) 정수
// sqrt≈max+0.5·min, atan2≈x/(1+0.28x²)
//------------------------------------------------------------------------------
static int16_t compute_mpu_pitch(int16_t ax,int16_t ay,int16_t az) {
const float G=4096.0f;
float x=(float)ax/G, y=(float)ay/G, z=(float)az/G;
float ay0=y<0?-y:y, az0=z<0?-z:z;
float vmax= ay0>az0? ay0:az0;
float vmin= ay0>az0? az0:ay0;
float denom = vmax + 0.5f*vmin + 1e-6f;
float r = x/denom;
float rad = r/(1.0f + 0.28f*r*r);
float deg = rad * 57.2957795f;
return (int16_t)(deg + (deg>=0?0.5f:-0.5f));
}
//------------------------------------------------------------------------------
// Text LCD 제어 함수들 (기존 코드 그대로)
//------------------------------------------------------------------------------
void LCD_WriteCommand(uint8_t cmd) {
uint8_t high = cmd & 0xF0;
uint8_t low = (cmd<<4)& 0xF0;
uint8_t seq[4] = {
high|0x0C, high|0x08,
low |0x0C, low |0x08
};
XIic_Send(iic_inst.BaseAddress, LCD_ADDR, seq, 4, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
void LCD_WriteData(uint8_t d) {
uint8_t high = d & 0xF0;
uint8_t low = (d <<4)& 0xF0;
uint8_t seq[4] = {
high|0x0D, high|0x08,
low |0x0D, low |0x08
};
XIic_Send(iic_inst.BaseAddress, LCD_ADDR, seq, 4, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
void LCD_Init(void) {
msleep(50);
LCD_WriteCommand(0x33); msleep(5);
LCD_WriteCommand(0x32);
LCD_WriteCommand(0x28);
LCD_WriteCommand(0x0F);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01); msleep(5);
}
void LCD_GotoXY(uint8_t row, uint8_t col) {
uint8_t addr = 0x80 | (row<<6) | col;
LCD_WriteCommand(addr);
}
// 부호 포함 5자리 찍기
void LCD_WriteSignedInt(int16_t v) {
char tmp[6];
// "%+5d" → 항상 부호, 총 5칸
sprintf(tmp, "%+5d", v);
for (int i = 0; i < 5; i++) {
LCD_WriteData((uint8_t)tmp[i]);
}
}
//------------------------------------------------------------------------------
// OLED 제어 함수들
//------------------------------------------------------------------------------
// Control + 명령어 1바이트
void oled_write_cmd(uint8_t cmd) {
uint8_t buf[2] = { OLED_CTL_CMD, cmd };
XIic_Send(iic_inst.BaseAddress, OLED_ADDR, buf, 2, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
// Control + 데이터 1바이트
void oled_write_data(uint8_t d) {
uint8_t buf[2] = { OLED_CTL_DAT, d };
XIic_Send(iic_inst.BaseAddress, OLED_ADDR, buf, 2, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
}
// SSD1306 초기화 (페이지 주소 모드 포함)
void oled_init(void) {
const uint8_t seq[] = {
0xAE, // display off
0x20,0x02, // page addressing mode
0xB0, // page0
0x00,0x10, // col addr = 0
0x40, // start line = 0
0x81,0x7F, // contrast
0xA1, // seg remap
0xC8, // com scan dec
0xA6, // normal display
0xA8,0x3F, // multiplex 1/64
0xD3,0x00, // display offset
0xD5,0x80, // clk div
0xD9,0x22, // pre-charge
0xDA,0x12, // com pins
0xDB,0x20, // vcomh
0x8D,0x14, // charge pump on
0xAF // display on
};
for (int i = 0; i < (int)sizeof(seq); i++) {
oled_write_cmd(seq[i]);
}
}
// 전체 화면 클리어
void oled_clear(void) {
for (int p = 0; p < 8; p++) {
oled_write_cmd(0xB0 | p);
oled_write_cmd(0x00);
oled_write_cmd(0x10);
for (int c = 0; c < 128; c++) {
oled_write_data(0x00);
}
}
}
//------------------------------------------------------------------------------
// 문자열(부호 포함 숫자) 출력 helper
//------------------------------------------------------------------------------
// page: 0~7, col: 문자 단위(0~15), s: null-terminated 문자열
void oled_puts_str(int page, int col, const char *s) {
// 1) 페이지·컬럼 설정
oled_write_cmd(0xB0 | page);
uint8_t x = col * 8;
oled_write_cmd(0x00 | (x & 0x0F));
oled_write_cmd(0x10 | ((x >> 4) & 0x0F));
// 2) 글자별 8바이트 폰트 전송
while (*s) {
uint8_t buf[9];
buf[0] = OLED_CTL_DAT;
// ASCII 0x20~0x7F 대응
memcpy(buf+1, font8x8_basic[(uint8_t)*s - 0x20], 8);
XIic_Send(iic_inst.BaseAddress, OLED_ADDR, buf, 9, XIIC_STOP);
while (XIic_IsIicBusy(iic_inst.BaseAddress));
s++;
}
}
//------------------------------------------------------------------------------
// OLED 픽셀 업데이트: 버퍼→실제 GDDRAM (한 컬럼만 갱신)
//------------------------------------------------------------------------------
void oled_update_column(int page, int col) {
oled_write_cmd(0xB0 | page); // page 설정
oled_write_cmd(0x00 | (col & 0x0F)); // col low
oled_write_cmd(0x10 | ((col>>4)&0x0F)); // col high
oled_write_data(oled_buf[page][col]); // 버퍼값 전송
}
//------------------------------------------------------------------------------
// (x,y) 에 1픽셀 그리기 (버퍼 갱신 + 해당 컬럼만 갱신)
//------------------------------------------------------------------------------
void oled_draw_pixel(int x, int y) {
if (x<0||x>127||y<0||y>63) return;
int page = y / 8;
int bit = y % 8;
oled_buf[page][x] |= (1<<bit); // 버퍼에 비트 세팅
oled_update_column(page, x); // 실제 화면에 바로 반영
}'Verilog_RTL 설계 > SoC' 카테고리의 다른 글
| [대한 상공 회의소] 광주 인력개발원_ 프로젝트 발표회 (1) | 2025.08.27 |
|---|---|
| [Project]Smart Mobility platform (4) | 2025.08.27 |
| [MicroBlaze] DHT11_iic (3) | 2025.07.29 |
| [MicroBlaze] Button_control (1) | 2025.07.28 |
| [MicroBlaze] fnd_control (1) | 2025.07.28 |