EECS 192: Mechatronics Design Lab Discussion 9 (Part 2): Embedded - - PowerPoint PPT Presentation

eecs 192 mechatronics design lab
SMART_READER_LITE
LIVE PREVIEW

EECS 192: Mechatronics Design Lab Discussion 9 (Part 2): Embedded - - PowerPoint PPT Presentation

EECS 192: Mechatronics Design Lab Discussion 9 (Part 2): Embedded Software written by: Richard Ducky Lin Spring 2015 18 & 19 Feb 2015 (Week 9) 1 Embedded Programming 2 Software Engineering Ducky (UCB EECS) Mechatronics Design Lab 18


slide-1
SLIDE 1

EECS 192: Mechatronics Design Lab

Discussion 9 (Part 2): Embedded Software written by: Richard ”Ducky” Lin Spring 2015 18 & 19 Feb 2015 (Week 9)

1 Embedded Programming 2 Software Engineering

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 1 / 13

slide-2
SLIDE 2

Embedded Programming

Embedded Programming

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 2 / 13

slide-3
SLIDE 3

Embedded Programming Limitations

Hardware Specs

Recall the hardware specs for your boards:

◮ MKL25Z128VLK4 microcontroller

◮ 48MHz ARM Cortex-M0+ ◮ 128KB flash ◮ 16KB SRAM

What might make embedded programming different from desktop programming? FRDM-KL25Z Board

image from KL25Z User’s Manual Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 3 / 13

slide-4
SLIDE 4

Embedded Programming Limitations

Memory Use

Say, I want to allocate some storage when I read my camera array.

uint16_t* read_camera () { uint16_t* camera_data = malloc (2* CAMERA_PIXELS ); for (int i=0; i< CAMERA_PIXELS ; i++) { camera_data [i] = camera_read_pixel (); } return camera_data ; }

Why might this be a bad idea on a microcontroller?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 4 / 13

slide-5
SLIDE 5

Embedded Programming Limitations

Memory Use

Say, I want to allocate some storage when I read my camera array.

uint16_t* read_camera () { uint16_t* camera_data = malloc (2* CAMERA_PIXELS ); for (int i=0; i< CAMERA_PIXELS ; i++) { camera_data [i] = camera_read_pixel (); } return camera_data ; }

Why might this be a bad idea on a microcontroller?

◮ Not checking for malloc failures - can return NULL

◮ (this isn’t an embedded-specific issue!)

◮ Dynamic (heap) memory allocation (malloc/free) is expensive ◮ Can cause heap fragmentation, especially when memory is scarce

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 4 / 13

slide-6
SLIDE 6

Embedded Programming Limitations

Memory Use

Ok, so malloc is bad. I’m more an object-oriented C++ guy anyways!

CameraArray * read_camera () { CameraArray * camera_data = new CameraArray (); camera_data ->read_from(near_cam); return camera_data ; } class CameraArray { public: void read_from(Camera& camera); int8_t get_line_error (); protected: uint16_t camera_data [ CAMERA_PIXELS ]; }

Why is this also bad?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 5 / 13

slide-7
SLIDE 7

Embedded Programming Limitations

Memory Use

Ok, so malloc is bad. I’m more an object-oriented C++ guy anyways!

CameraArray * read_camera () { CameraArray * camera_data = new CameraArray (); camera_data ->read_from(near_cam); return camera_data ; } class CameraArray { public: void read_from(Camera& camera); int8_t get_line_error (); protected: uint16_t camera_data [ CAMERA_PIXELS ]; }

Why is this also bad?

◮ new also does dynamic memory allocation

◮ So exactly the same issues as malloc, but perhaps a bit more sneaky Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 5 / 13

slide-8
SLIDE 8

Embedded Programming Limitations

Pass-By-Value

Ok enough with dynamic memory allocation. No new either.

CameraArray read_camera ( CameraArray camera_data ) { camera_data .read_from(near_cam); return camera_data ; } class CameraArray { public: void read_from(Camera& camera); int8_t get_line_error (); protected: uint16_t camera_data [ CAMERA_PIXELS ]; }

What performance issues might arise from this?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 6 / 13

slide-9
SLIDE 9

Embedded Programming Limitations

Pass-By-Value

Ok enough with dynamic memory allocation. No new either.

CameraArray read_camera ( CameraArray camera_data ) { camera_data .read_from(near_cam); return camera_data ; } class CameraArray { public: void read_from(Camera& camera); int8_t get_line_error (); protected: uint16_t camera_data [ CAMERA_PIXELS ]; }

What performance issues might arise from this?

◮ C++ arguments are passed by value - it may create a copy

◮ Copying large data structures is inefficient and can cause subtle bugs

◮ Pass pointers to objects or use references instead

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 6 / 13

slide-10
SLIDE 10

Embedded Programming Limitations

Memory Use

Ok, let’s say I write a recursive image processing algorithm.

Bear with me on this crappy example; I’m not a CV guy uint8_t difference_gaussians (uint8_t level , uint16_t [] line_data) { uint16_t line_filtered [ CAMERA_PIXELS ]; gaussian_blur ( line_filtered /* dst */, line_data /* src */); if (level != 0) { uint8_t next_result = difference_gaussians (level -1, line_filtered ); } return /*CV magic on filtered and

  • riginal

line data */; }

So what can go wrong here?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 7 / 13

slide-11
SLIDE 11

Embedded Programming Limitations

Memory Use

Ok, let’s say I write a recursive image processing algorithm.

Bear with me on this crappy example; I’m not a CV guy uint8_t difference_gaussians (uint8_t level , uint16_t [] line_data) { uint16_t line_filtered [ CAMERA_PIXELS ]; gaussian_blur ( line_filtered /* dst */, line_data /* src */); if (level != 0) { uint8_t next_result = difference_gaussians (level -1, line_filtered ); } return /*CV magic on filtered and

  • riginal

line data */; }

So what can go wrong here?

◮ Potential stack overflow if recursion runs deep enough

◮ Each recursive call allocates a 2*CAMERA PIXELS array on stack ◮ Possibly undetected (no operating system or memory protection)! Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 7 / 13

slide-12
SLIDE 12

Embedded Programming Limitations

Synchronization

Ok, let’s talk threads!

uint16_t camera_data [ CAMERA_PIXELS ]; void camera_read_thread () { for (int i=0; i< CAMERA_PIXELS ; i++) { camera_data [i] = camera_read_pixel (); } Thread.wait( INTEGRATION_TIME ); } void camera_process_thread () { uint8_t line_camera_distance = /* magic filter */; servo_pwm.write(kp * line_camera_distance ); }

What might happen?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 8 / 13

slide-13
SLIDE 13

Embedded Programming Limitations

Synchronization

Ok, let’s talk threads!

uint16_t camera_data [ CAMERA_PIXELS ]; void camera_read_thread () { for (int i=0; i< CAMERA_PIXELS ; i++) { camera_data [i] = camera_read_pixel (); } Thread.wait( INTEGRATION_TIME ); } void camera_process_thread () { uint8_t line_camera_distance = /* magic filter */; servo_pwm.write(kp * line_camera_distance ); }

What might happen?

◮ No synchronization! Can read data in the middle of a write!

◮ Might get half of one frame and half of another... Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 8 / 13

slide-14
SLIDE 14

Embedded Programming Limitations

Synchronization

Ok, let’s talk threads!

uint16_t camera_data [ CAMERA_PIXELS ]; void camera_read_thread () { for (int i=0; i< CAMERA_PIXELS ; i++) { camera_data [i] = camera_read_pixel (); } Thread.wait( INTEGRATION_TIME ); } void camera_process_thread () { uint8_t line_camera_distance = /* magic filter */; servo_pwm.write(kp * line_camera_distance ); }

How do I prevent it?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 8 / 13

slide-15
SLIDE 15

Embedded Programming Limitations

Synchronization

Ok, let’s talk threads!

uint16_t camera_data [ CAMERA_PIXELS ]; void camera_read_thread () { for (int i=0; i< CAMERA_PIXELS ; i++) { camera_data [i] = camera_read_pixel (); } Thread.wait( INTEGRATION_TIME ); } void camera_process_thread () { uint8_t line_camera_distance = /* magic filter */; servo_pwm.write(kp * line_camera_distance ); }

How do I prevent it?

◮ Various synchronization constructs: mutexes/locks, semaphores, ... ◮ Nonblocking solutions: double/triple buffering

◮ Or asynchronous FIFOs (efficiently implemented as a circular buffer) Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 8 / 13

slide-16
SLIDE 16

Software Engineering

Software Engineering

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 9 / 13

slide-17
SLIDE 17

Software Engineering Expectations

Two Cameras

I have some code to read a single camera.

Camera near_cam(PTB2 /* CLK */, PTB3 /*SI*/, PTC2 /*AO*/); void control_loop () { servo_pwm.write(kp * near_cam. get_line_distance ()); }

Given the structure, how would I add another camera?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 10 / 13

slide-18
SLIDE 18

Software Engineering Expectations

Two Cameras

I have some code to read a single camera.

Camera near_cam(PTB2 /* CLK */, PTB3 /*SI*/, PTC2 /*AO*/); void control_loop () { servo_pwm.write(kp * near_cam. get_line_distance ()); }

Given the structure, how would I add another camera?

◮ Simple, right? Instantiate another Camera?

◮ Camera far cam(PTB4, PTB5, PTC1); Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 10 / 13

slide-19
SLIDE 19

Software Engineering Expectations

Two Cameras

I have some code to read a single camera.

Camera near_cam(PTB2 /* CLK */, PTB3 /*SI*/, PTC2 /*AO*/); void control_loop () { servo_pwm.write(kp * near_cam. get_line_distance ()); }

Given the structure, how would I add another camera?

◮ Simple, right? Instantiate another Camera?

◮ Camera far cam(PTB4, PTB5, PTC1);

What hidden assumptions / expectations did I have for Camera?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 10 / 13

slide-20
SLIDE 20

Software Engineering Expectations

Expectations

What if the Camera implementation looked like this?

uint16_t camera_data [ CAMERA_PIXELS ]; // global class Camera { public: Camera(PinName clk , PinName si , PinName adc); void read () { /* ADC reads into global camera_data */ } int8_t get_line_distance () { return /* some computation

  • n global

camera_data */; } }

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 11 / 13

slide-21
SLIDE 21

Software Engineering Expectations

Expectations

What if the Camera implementation looked like this?

uint16_t camera_data [ CAMERA_PIXELS ]; // global class Camera { public: Camera(PinName clk , PinName si , PinName adc); void read () { /* ADC reads into global camera_data */ } int8_t get_line_distance () { return /* some computation

  • n global

camera_data */; } }

OH SH-

◮ Breaks user expectations of object encapsulation and independence

◮ DON’T DO IT! Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 11 / 13

slide-22
SLIDE 22

Software Engineering Expectations

Globals

While we’re talking about globals, what anti-patterns can arise from this?

float motor_velocity_target ; // global void main () { motor_velocity_target = 3.0; // rest of code here }

So far, so good, right?

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 12 / 13

slide-23
SLIDE 23

Software Engineering Expectations

Globals

While we’re talking about globals, what anti-patterns can arise from this?

float motor_velocity_target ; // global void main () { motor_velocity_target = 3.0; // rest of code here }

So far, so good, right? Perhaps I also have a kill switch in another function: if (kill switch) motor velocity target = 0;

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 12 / 13

slide-24
SLIDE 24

Software Engineering Expectations

Globals

While we’re talking about globals, what anti-patterns can arise from this?

float motor_velocity_target ; // global void main () { motor_velocity_target = 3.0; // rest of code here }

So far, so good, right? Perhaps I also have a kill switch in another function: if (kill switch) motor velocity target = 0; And why not have it dependent on tracking, perhaps in a different .c file: if (bad tracking) motor velocity target -= 0.1;

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 12 / 13

slide-25
SLIDE 25

Software Engineering Expectations

Globals

While we’re talking about globals, what anti-patterns can arise from this?

float motor_velocity_target ; // global void main () { motor_velocity_target = 3.0; // rest of code here }

So far, so good, right? Perhaps I also have a kill switch in another function: if (kill switch) motor velocity target = 0; And why not have it dependent on tracking, perhaps in a different .c file: if (bad tracking) motor velocity target -= 0.1; Soon, you have no clue what the target actually is - dataflow spaghetti!

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 12 / 13

slide-26
SLIDE 26

Software Engineering Expectations

Summary

◮ Avoid dynamic memory allocation ◮ Watch out for the limited RAM and stack overflow ◮ Watch out for synchronization errors ◮ Write code that conforms to user expectations ◮ Avoid dataflow spaghetti

Ducky (UCB EECS) Mechatronics Design Lab 18 & 19 Feb 2015 (Week 9) 13 / 13