Week 13 - Output Devices

Contents

Draw

Considering about my final project, I had to control five servos at the same time. According to response efficiency and pinouts of Arduino/Satshakit, it would be better to make servo modules to control servos by one on one. The master control board will send data over I2C signal to each module and the slave modules should generate PWM signal to servo directly.

The module node would be designed on KiCAD and be using Attiny44A as controller. Starting with editing footprint of XC1109CT-ND crystal, I was able to import to my PCB circuit board design.

By checking Attiny44A data sheet, TWI/I2C protocol should be applied over PA4(SCL) and PA6(SDA) and the rest hardware PWM pin would be PA7. A bad news to our project is that PA7 can only provide 8-bit PWM. (talk more later in "PWM signal")

Here is first verion of attiny44a_servo_i2c.

attiny44a_servo_i2c_traces.png attiny44a_servo_i2c_cutout.png

Missing most of 3D libraries

After making it out with MDX-20, I just realised that I had to let the communication be serial. The 4 pinouts on the right-side is going to talk over I2C(SDL/SDA) and get powered up by 5V and GND, but the problem is that it is more difficult to make a serial flat cable in 1x4 instead of 2x3. For this reason, I moved on to design a second version with 2x3 serial pinouts.

There was very interesting part that, in fact, 2x3 pinouts over I2C can just simply reuse 2x3 ISP header because of multiple functions at same pinouts (MOSI & SDA, SCK & SCL) on Attiny44A. However I made another 2x3 instead of using original ISP header since I did not want to deal with RST at this time and I wanted to make a prototype as soon as possible.

attiny44a_servo_i2c_v2_traces.png attiny44a_servo_i2c_v2_cutout.png

Make

Keep making secong version of attiny44a_servo_i2c with MDX-20.

Comparing v1 and v2

PWM signal

Looking at data sheet again and hello.servo.44 hardware PWM example by Neil Gershenfeld, I found out that in the example it generate PWM signal by PA6 in 16-bit, but my PA6 was busy talking over I2C. In this case, I could just use PA7 in 8-bit resolution instead of PA6 in 16-bit.

It took me about a week to understand how could I control Attiny44A to get PWM signal.

I modified hello.servo.44 example

#include <avr/io.h>
#include <util/delay.h>

#define position_delay() _delay_ms(1000)

#define PWM_port PORTA
#define PWM_pin (1 << PA7)
#define PWM_direction DDRA

int main(void) {
   
   TCCR0A = (1 << COM0B1) | (0 << COM0B0) | (0 << WGM01) | (1 << WGM00); // clear OC0B on compare match
   TCCR0B = (1 << CS02) | (0 << CS01) | (1 << CS00) | (1 << WGM02); // prescaler /1024,
    
   TCNT0 = 0x00;
   OCR0A = 0xC3;
   OCR0B = 0x05;
   //
   PORTA |= (1 << PA7);
   DDRA |= (1 << PA7);
   //
   // main loop
   //
   while (1) {
      //
      // 1 ms PWM on time
      //
      OCR0B = 0x05;
      position_delay();
      //
      // 2 ms PWM on time
      //
      OCR0B = 0x18;
      position_delay();
      //
      // 1.5 ms PWM on time
      //
      OCR0B = 0x0F;
      position_delay();
   }
}

In the code, Attiny44A internal counter would start from TCNT0(0x00). Default counter will count to 255(0xFF) but here I set top interrupt flag OCR0A to 195(0xC3).

Calculation:

For PWM signal, it must generate 20ms time period(50Hz) including 0.5ms - 2ms high level. To get this frequency, follow this equation

fclk = 20 MHz, N = 1024

So it would get frequency equals to 38.30Hz which would let us get 26.10ms time period when it count from 0 to default top 255(0xFF). Therefore, if I set top interrupt OCR0A to 195(0xC3), it would generate 20ms signal time period. Boom!

Another interrupt flag OCR0B could control that logic high in 0.5ms to about 2ms. As you can see, in the main loop, I could set OCR0B from 5(0x05) to 24(0x18) which will map within 0 to 180 degrees. Each bit can only control (180-0)/(24-5) ~= 9.5 degrees.

Result

Files