forked from M-Labs/artiq-zynq
Compare commits
10 Commits
15995bf593
...
e8fd35476d
Author | SHA1 | Date | |
---|---|---|---|
e8fd35476d | |||
50b9a7977c | |||
f8ebfb4340 | |||
5fd9b97e78 | |||
34ba53b7da | |||
8ab2060f18 | |||
bc7925989b | |||
b2256800fe | |||
637163bbca | |||
50ead76c09 |
161
cxp_img_kernel.py
Normal file
161
cxp_img_kernel.py
Normal file
@ -0,0 +1,161 @@
|
||||
from artiq.language.core import kernel
|
||||
from artiq.experiment import *
|
||||
from artiq.coredevice.cxp_grabber import write_pgm, write_file
|
||||
|
||||
from numpy import array
|
||||
|
||||
# boA2448-250cm XML specific
|
||||
_USER_SET_SELECTOR = 0x10000050
|
||||
_REAL_ACQ_MODE = 0x10000BB4
|
||||
_REAL_ACQ_START = 0x10000498
|
||||
_REAL_ACQ_STOP = 0x100004A4
|
||||
_REAL_ACQ_ABORT = 0x100004B0
|
||||
_BSL_SENSOR_ON = 0x100004D4
|
||||
_BSL_SENSOR_STAND_BY = 0x100004C8
|
||||
_BSL_SENSOR_OFF = 0x100004BC
|
||||
_BSL_POWER_MODE = 0x100000B4
|
||||
_TRIG_MODE_INDEX = 0x10001424
|
||||
_TRIG_SRC_INDEX = 0x100081AC
|
||||
_TRIG_ACT_INDEX = 0x1000293C
|
||||
_TRIG_SOFTWARE_INDEX = 0x10000C34
|
||||
_TEST_PATTERN = 0x10003500
|
||||
_PIXEL_FORMAT = 0x100078B4
|
||||
|
||||
|
||||
class CXP_Kernel(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.setattr_device("led0")
|
||||
self.setattr_device("cxp")
|
||||
|
||||
self.gate_mask = 0b1111_1111
|
||||
self.cnt = [0] * self.gate_mask.bit_count()
|
||||
|
||||
self.width, self.height = 2448, 3
|
||||
self.frame = array([[0] * self.width])
|
||||
|
||||
@kernel
|
||||
def camera_sensor_off(self):
|
||||
# Abort any acq as it cannot accept sensor off control message in acq mode
|
||||
self.cxp.write32(_REAL_ACQ_ABORT, 1)
|
||||
self.cxp.write32(_BSL_SENSOR_OFF, 1)
|
||||
|
||||
@kernel
|
||||
def camera_trigger_nonrealtime_setup(self, pixel_format, test_pattern):
|
||||
# acq ABORT to clear any previous untrigger acq
|
||||
self.cxp.write32(_REAL_ACQ_ABORT, 1)
|
||||
|
||||
self.cxp.write32(_PIXEL_FORMAT, pixel_format)
|
||||
|
||||
# TEST pattern setup
|
||||
self.cxp.write32(_TEST_PATTERN, test_pattern)
|
||||
|
||||
# TRIGGER setup
|
||||
self.cxp.write32(_TRIG_MODE_INDEX, 1) # ON
|
||||
self.cxp.write32(_TRIG_SRC_INDEX, 7) # CXPTrigger0
|
||||
self.cxp.write32(_TRIG_ACT_INDEX, 2) # trig on any-edge
|
||||
|
||||
# Ready to accept trigger
|
||||
self.cxp.write32(_REAL_ACQ_MODE, 1) # set single frame mode
|
||||
self.cxp.write32(_REAL_ACQ_START, 1) # single acq start
|
||||
|
||||
# Print out current transfer bit depth
|
||||
_BSL_TRANSFER_BITDEPTH_MODE = 0x10001B8C # d_49
|
||||
_BSL_TRANSFER_BITDEPTH = 0x10001C10 # d_50
|
||||
# print(self.cxp.read32(_BSL_TRANSFER_BITDEPTH))
|
||||
|
||||
@kernel
|
||||
def camera_trig_and_get_roi(self, standalone=False):
|
||||
if standalone:
|
||||
self.core.reset()
|
||||
self.cxp.setup_roi(0, 0, 0, 1, 1)
|
||||
self.cxp.setup_roi(1, 0, 2047, 1, 2048)
|
||||
self.cxp.setup_roi(2, 2443, 2047, 2448, 2048)
|
||||
self.cxp.setup_roi(3, 2443, 0, 2448, 1)
|
||||
self.cxp.setup_roi(4, 1, 1, 5, 5)
|
||||
self.cxp.setup_roi(5, 1, 1, 4, 4)
|
||||
self.cxp.setup_roi(6, 1, 1, 3, 3)
|
||||
self.cxp.setup_roi(7, 1, 1, 2, 2)
|
||||
self.cxp.gate_roi(self.gate_mask)
|
||||
delay(1 * ms)
|
||||
|
||||
self.cxp.send_cxp_linktrigger(0) # CXPTrigger0
|
||||
self.core.wait_until_mu(now_mu() + 10_000_000)
|
||||
self.cxp.input_mu(self.cnt, -1)
|
||||
|
||||
@kernel
|
||||
def download_xml(self):
|
||||
buffer = [0] * (102400 // 4)
|
||||
self.cxp.read_local_xml(buffer)
|
||||
write_file(buffer, "camera_setting.zip")
|
||||
|
||||
@kernel
|
||||
def capture_full_frame(self, pixel_format, test_pattern):
|
||||
width, height = 2448, 2048
|
||||
frame_matrix = array([[0] * width] * height)
|
||||
line_buffer = array([[0] * width])
|
||||
|
||||
self.cxp.write32(_REAL_ACQ_ABORT, 1)
|
||||
self.cxp.write32(_PIXEL_FORMAT, pixel_format)
|
||||
self.cxp.write32(_TEST_PATTERN, test_pattern)
|
||||
# TRIGGER setup
|
||||
self.cxp.write32(_TRIG_MODE_INDEX, 1) # ON
|
||||
self.cxp.write32(_TRIG_SRC_INDEX, 7) # CXPTrigger0
|
||||
self.cxp.write32(_TRIG_ACT_INDEX, 2) # trig on any-edge
|
||||
# Ready to accept trigger
|
||||
# self.cxp.write32(_REAL_ACQ_MODE, 1) # set single frame mode
|
||||
self.cxp.write32(_REAL_ACQ_MODE, 0) # Continuously accept trigger
|
||||
self.cxp.write32(_REAL_ACQ_START, 1)
|
||||
|
||||
# Send trigger
|
||||
self.core.reset()
|
||||
pixel_width = 0
|
||||
for line in range(height):
|
||||
self.cxp.start_roi_viewer(0, line, 2448, line + 1)
|
||||
delay(30 * ms)
|
||||
self.cxp.send_cxp_linktrigger(0) # CXPTrigger0
|
||||
# Wait until fifo is full
|
||||
# MONO8 = 10 milliseconds
|
||||
# MONO10 = 11 milliseconds
|
||||
# MONO12 = 12 milliseconds
|
||||
self.core.wait_until_mu(now_mu() + 12_000_000)
|
||||
pixel_width = self.cxp.read_roi_viewer_frame(line_buffer)
|
||||
for x in range(width):
|
||||
frame_matrix[line][x] = (
|
||||
line_buffer[0][x]
|
||||
if pixel_width == 8
|
||||
else line_buffer[0][x] << 16 - pixel_width
|
||||
)
|
||||
|
||||
self.cxp.write32(_REAL_ACQ_STOP, 1)
|
||||
write_pgm(frame_matrix, "./test.pgm", 8 if pixel_width == 8 else 16)
|
||||
|
||||
def run(self):
|
||||
# self.download_xml()
|
||||
|
||||
# Following GenICam PFNC standard
|
||||
pixel_formats = {
|
||||
"MONO8": 17301505,
|
||||
"MONO10": 17825795, # MONO10 unpacked
|
||||
"MONO12": 17825797, # MONO12 unpacked
|
||||
}
|
||||
# see https://docs.baslerweb.com/test-patterns
|
||||
test_patterns = {
|
||||
"OFF": 0, # normal operation
|
||||
"BLACK": 1, # all pixels set to 0
|
||||
"WHITE": 2, # all pixels set to (2^N)-1
|
||||
"TESTIMAGE1": 3, # fixed diagonal gray gradients ranging from 0 to 255
|
||||
"TESTIMAGE2": 4,
|
||||
"TESTIMAGE3": 5, # moving gradient ranging from 0 to 4095
|
||||
}
|
||||
|
||||
if True:
|
||||
pix_fmt, test_pat = "MONO8", "TESTIMAGE1"
|
||||
print(f"Capturing full frame {pix_fmt} {test_pat} pattern")
|
||||
self.capture_full_frame(pixel_formats[pix_fmt], test_patterns[test_pat])
|
||||
else:
|
||||
for fmt_name, fmt_code in pixel_formats.items():
|
||||
self.camera_trigger_nonrealtime_setup(fmt_code, test_patterns["WHITE"])
|
||||
self.camera_trig_and_get_roi(True)
|
||||
print(f"{fmt_name} count list = {self.cnt}")
|
||||
self.camera_sensor_off()
|
16
flake.lock
generated
16
flake.lock
generated
@ -11,11 +11,11 @@
|
||||
"src-pythonparser": "src-pythonparser"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1741925484,
|
||||
"narHash": "sha256-sovpN0Ii+GOVY4Qku8eal5hWATScCc1VbYnfdwc88dQ=",
|
||||
"lastModified": 1742274039,
|
||||
"narHash": "sha256-K0Np2ZvtB37Gq1ZbKf1mfSWfMbtPjZVdKtOHAS/Pauk=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "cdacbf71c569b0968c81fb358838e9b69946af26",
|
||||
"revCount": 9192,
|
||||
"rev": "9e655332e35bac5c365e80c0c595767f5953bcb3",
|
||||
"revCount": 9198,
|
||||
"type": "git",
|
||||
"url": "https://github.com/m-labs/artiq.git"
|
||||
},
|
||||
@ -229,11 +229,11 @@
|
||||
"rust-overlay": "rust-overlay_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1741773807,
|
||||
"narHash": "sha256-pywveAL5C0Jl+qo8XmgAL11RfrnSW/Afu3Yg1K/lAcU=",
|
||||
"lastModified": 1742368409,
|
||||
"narHash": "sha256-da8QiftpLR/UsNFc0vGczg+EGAHgxuQCvy2QWhRfYVk=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "905b97431de37ccc61f8babd7def99627578a843",
|
||||
"revCount": 694,
|
||||
"rev": "671cfc894c0c285008a8830b3c32cd62cce881a0",
|
||||
"revCount": 695,
|
||||
"type": "git",
|
||||
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
|
||||
},
|
||||
|
@ -122,7 +122,11 @@ impl IoExpander {
|
||||
// Check for ack from io expander
|
||||
self.select(i2c)?;
|
||||
i2c.start()?;
|
||||
let ack = i2c.write(self.address)?;
|
||||
let ack = match i2c.write(self.address) {
|
||||
Ok(()) => true,
|
||||
Err(i2c::Error::Nack) => false,
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
i2c.stop()?;
|
||||
Ok(ack)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use core::result;
|
||||
|
||||
use embedded_hal::blocking::delay::DelayUs;
|
||||
use libboard_zynq::{i2c::I2c, time::Milliseconds, timer::GlobalTimer};
|
||||
use libboard_zynq::{i2c::{Error as I2cError, I2c},
|
||||
time::Milliseconds,
|
||||
timer::GlobalTimer};
|
||||
use log::info;
|
||||
|
||||
#[cfg(not(si5324_soft_reset))]
|
||||
@ -97,15 +99,18 @@ fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySetti
|
||||
|
||||
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||
i2c.start().unwrap();
|
||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||
return Err("Si5324 failed to ack write address");
|
||||
}
|
||||
if !i2c.write(reg).unwrap() {
|
||||
return Err("Si5324 failed to ack register");
|
||||
}
|
||||
if !i2c.write(val).unwrap() {
|
||||
return Err("Si5324 failed to ack value");
|
||||
}
|
||||
i2c.write(ADDRESS << 1).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack write address",
|
||||
err => err.into(),
|
||||
})?;
|
||||
i2c.write(reg).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack register",
|
||||
err => err.into(),
|
||||
})?;
|
||||
i2c.write(val).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack value",
|
||||
err => err.into(),
|
||||
})?;
|
||||
i2c.stop().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
@ -113,29 +118,37 @@ fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||
#[allow(dead_code)]
|
||||
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||
i2c.start().unwrap();
|
||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||
return Err("Si5324 failed to ack write address");
|
||||
i2c.write(ADDRESS << 1).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack write address",
|
||||
err => err.into(),
|
||||
})?;
|
||||
i2c.write(reg).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack register",
|
||||
err => err.into(),
|
||||
})?;
|
||||
match i2c.write(val) {
|
||||
Ok(()) | Err(I2cError::Nack) => (),
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
if !i2c.write(reg).unwrap() {
|
||||
return Err("Si5324 failed to ack register");
|
||||
}
|
||||
i2c.write(val).unwrap();
|
||||
i2c.stop().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> {
|
||||
i2c.start().unwrap();
|
||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||
return Err("Si5324 failed to ack write address");
|
||||
}
|
||||
if !i2c.write(reg).unwrap() {
|
||||
return Err("Si5324 failed to ack register");
|
||||
}
|
||||
i2c.write(ADDRESS << 1).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack write address",
|
||||
err => err.into(),
|
||||
})?;
|
||||
i2c.write(reg).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack register",
|
||||
err => err.into(),
|
||||
})?;
|
||||
i2c.restart().unwrap();
|
||||
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
|
||||
return Err("Si5324 failed to ack read address");
|
||||
}
|
||||
i2c.write((ADDRESS << 1) | 1).map_err(|err| match err {
|
||||
I2cError::Nack => "Si5324 failed to ack read address",
|
||||
err => err.into(),
|
||||
})?;
|
||||
let val = i2c.read(false).unwrap();
|
||||
i2c.stop().unwrap();
|
||||
Ok(val)
|
||||
|
@ -46,7 +46,9 @@ impl fmt::Display for Error {
|
||||
&Error::ROISizeTooBig(width, height) => {
|
||||
write!(
|
||||
f,
|
||||
"ROISizeTooBig - The maximum ROIViewer size is {} pixels but the ROI is set to {} ({}x{}) pixels",
|
||||
"ROISizeTooBig - The maximum ROIViewer height and total size are {} and {} pixels respectively \
|
||||
but the ROI is set to {} ({}x{}) pixels",
|
||||
ROI_MAX_SIZE / 4,
|
||||
ROI_MAX_SIZE,
|
||||
width * height,
|
||||
width,
|
||||
@ -170,9 +172,9 @@ pub extern "C" fn write32(addr: i32, val: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "C" fn start_roi_viewer(x0: i32, x1: i32, y0: i32, y1: i32) {
|
||||
pub extern "C" fn start_roi_viewer(x0: i32, y0: i32, x1: i32, y1: i32) {
|
||||
let (width, height) = ((x1 - x0) as usize, (y1 - y0) as usize);
|
||||
if width * height > ROI_MAX_SIZE {
|
||||
if width * height > ROI_MAX_SIZE || height > ROI_MAX_SIZE / 4 {
|
||||
artiq_raise!("CXPError", format!("{}", Error::ROISizeTooBig(width, height)));
|
||||
} else {
|
||||
unsafe {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use libboard_zynq::i2c::I2c;
|
||||
use libboard_zynq::i2c::{Error, I2c};
|
||||
|
||||
use crate::artiq_raise;
|
||||
|
||||
@ -38,7 +38,8 @@ pub extern "C" fn write(busno: i32, data: i32) -> bool {
|
||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
||||
}
|
||||
match get_bus().write(data as u8) {
|
||||
Ok(r) => r,
|
||||
Ok(()) => true,
|
||||
Err(Error::Nack) => false,
|
||||
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,10 @@ use libboard_artiq::{drtio_routing, drtioaux,
|
||||
pl::csr};
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
use libboard_zynq::error_led::ErrorLED;
|
||||
use libboard_zynq::{i2c::I2c, print, println, slcr, time::Milliseconds, timer::GlobalTimer};
|
||||
use libboard_zynq::{i2c::{Error as I2cError, I2c},
|
||||
print, println, slcr,
|
||||
time::Milliseconds,
|
||||
timer::GlobalTimer};
|
||||
use libconfig::Config;
|
||||
use libcortex_a9::{l2c::enable_l2_cache, regs::MPIDR};
|
||||
use libregister::RegisterR;
|
||||
@ -435,11 +438,18 @@ fn process_aux_packet(
|
||||
timer
|
||||
);
|
||||
match i2c.write(data) {
|
||||
Ok(ack) => drtioaux::send(
|
||||
Ok(()) => drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::I2cWriteReply {
|
||||
succeeded: true,
|
||||
ack: ack,
|
||||
ack: true,
|
||||
},
|
||||
),
|
||||
Err(I2cError::Nack) => drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::I2cWriteReply {
|
||||
succeeded: true,
|
||||
ack: false,
|
||||
},
|
||||
),
|
||||
Err(_) => drtioaux::send(
|
||||
|
Loading…
x
Reference in New Issue
Block a user