summaryrefslogtreecommitdiffhomepage
path: root/digital/asserv/src/asserv/pwm_ocr.avr.c
blob: 25f2cfc755d7d3603af8f760c4b0a12189bb20fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/* pwm_ocr.avr.c - PWM using internal generator. */
/* asserv - Position & speed motor control on AVR. {{{
 *
 * Copyright (C) 2008 Nicolas Schodet
 *
 * APBTeam:
 *        Web: http://apbteam.org/
 *      Email: team AT apbteam DOT org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * }}} */
#include "common.h"
#include "pwm_ocr.avr.h"
#include "pwm.h"

#include "modules/utils/utils.h"
#include "io.h"

/** Assign PWM outputs. */
#undef PWM1
#undef PWM2
#undef PWM3
#undef PWM4

#define PWM1_OCR OCR1B
#define PWM1_OCR_BIT 6
#define PWM1_DIR 4
#define PWM2_OCR OCR1C
#define PWM2_OCR_BIT 7
#define PWM2_DIR 5
#define PWM3_OCR OCR3B
#define PWM3_OCR_BIT 4
#define PWM3_DIR 2
#define PWM4_OCR OCR3C
#define PWM4_OCR_BIT 5
#define PWM4_DIR 3

#include "pwm_config.h"

/** PWM reverse direction, port B. */
static uint8_t pwm_ocr_dir_reverse_b;
/** PWM reverse direction, port E. */
static uint8_t pwm_ocr_dir_reverse_e;

/** Initialise PWM generator. */
void
pwm_ocr_init (void)
{
    /* Fast PWM, TOP = 0x3ff, OCnB & OCnC with positive logic.
       f_IO without prescaler.
       Fpwm = f_IO / (prescaler * (1 + TOP)) = 14400 Hz. */
#if PWM1or2
    TCCR1A =
	regv (COM1A1, COM1A0, COM1B1, COM1B0, COM1C1, COM1C0, WGM11, WGM10,
		   0,      0,      1,      0,      1,      0,     1,     1);
    TCCR1B = regv (ICNC1, ICES1, 5, WGM13, WGM12, CS12, CS11, CS10,
		       0,     0, 0,     0,     1,    0,    0,    1);
    /* Enable pwm and direction outputs in DDRB. */
    DDRB |= PWM1c (_BV (PWM1_OCR_BIT) | _BV (PWM1_DIR))
	| PWM2c (_BV (PWM2_OCR_BIT) | _BV (PWM2_DIR));
#endif
#if PWM3or4
    TCCR3A =
	regv (COM3A1, COM3A0, COM3B1, COM3B0, COM3C1, COM3C0, WGM31, WGM30,
		   0,      0,      1,      0,      1,      0,     1,     1);
    TCCR3B = regv (ICNC3, ICES3, 5, WGM33, WGM32, CS32, CS31, CS30,
		       0,     0, 0,     0,     1,    0,    0,    1);
    /* Enable pwm and direction outputs in DDRE. */
    DDRE |= PWM3c (_BV (PWM3_OCR_BIT) | _BV (PWM3_DIR))
	| PWM4c (_BV (PWM4_OCR_BIT) | _BV (PWM4_DIR));
#endif
}

/** Update the hardware PWM values. */
void
pwm_ocr_update (void)
{
#if PWM1or2
    uint8_t dir_b;
    /* Sample port B. */
    dir_b = PORTB & ~(PWM1c (_BV (PWM1_DIR)) | PWM2c (_BV (PWM2_DIR)));
# ifdef PWM1
    uint16_t pwm1;
    /* Set PWM1. */
    if (PWM_VALUE (PWM1) == 0)
      {
	pwm1 = 0;
      }
    else if (PWM_VALUE (PWM1) < 0)
      {
	pwm1 = -PWM_VALUE (PWM1);
      }
    else
      {
	dir_b |= _BV (PWM1_DIR);
	pwm1 = PWM_VALUE (PWM1);
      }
# endif /* PWM1 */
# ifdef PWM2
    uint16_t pwm2;
    /* Set PWM2. */
    if (PWM_VALUE (PWM2) == 0)
      {
	pwm2 = 0;
      }
    else if (PWM_VALUE (PWM2) < 0)
      {
	pwm2 = -PWM_VALUE (PWM2);
      }
    else
      {
	dir_b |= _BV (PWM2_DIR);
	pwm2 = PWM_VALUE (PWM2);
      }
# endif /* PWM2 */
#endif /* PWM1or2 */
#if PWM3or4
    uint8_t dir_e;
    /* Sample port E. */
    dir_e = PORTE & ~(PWM3c (_BV (PWM3_DIR)) | PWM4c (_BV (PWM4_DIR)));
# ifdef PWM3
    uint16_t pwm3;
    /* Set PWM3. */
    if (PWM_VALUE (PWM3) == 0)
      {
	pwm3 = 0;
      }
    else if (PWM_VALUE (PWM3) < 0)
      {
	pwm3 = -PWM_VALUE (PWM3);
      }
    else
      {
	dir_e |= _BV (PWM3_DIR);
	pwm3 = PWM_VALUE (PWM3);
      }
# endif /* PWM3 */
# ifdef PWM4
    uint16_t pwm4;
    /* Set PWM4. */
    if (PWM_VALUE (PWM4) == 0)
      {
	pwm4 = 0;
      }
    else if (PWM_VALUE (PWM4) < 0)
      {
	pwm4 = -PWM_VALUE (PWM4);
      }
    else
      {
	dir_e |= _BV (PWM4_DIR);
	pwm4 = PWM_VALUE (PWM4);
      }
# endif /* PWM4 */
#endif /* PWM3or4 */
    /* Setup registers. */
    /* Here, there could be a problem because OCRx are double buffered, not
     * PORTx! */
    /* Another problem arise if the OCR sampling is done between left and
     * right OCR: the right PWM is one cycle late. */
    /* A solution could be to use interrupts to update PWM or to synchronise
     * general timer with PWM. */
#if PWM1or2
    dir_b ^= pwm_ocr_dir_reverse_b;
    PORTB = dir_b;
# ifdef PWM1
    PWM1_OCR = pwm1;
# endif
# ifdef PWM2
    PWM2_OCR = pwm2;
# endif
#endif /* PWM1or2 */
#if PWM3or4
    dir_e ^= pwm_ocr_dir_reverse_e;
    PORTE = dir_e;
# ifdef PWM3
    PWM3_OCR = pwm3;
# endif
# ifdef PWM4
    PWM4_OCR = pwm4;
# endif
#endif /* PWM3or4 */
}

void
pwm_ocr_set_reverse (uint8_t reverse)
{
    pwm_reverse = reverse;
    pwm_ocr_dir_reverse_b = 0;
    pwm_ocr_dir_reverse_e = 0;
#ifdef PWM1
    if (reverse & PWM_REVERSE_BIT (PWM1))
	pwm_ocr_dir_reverse_b |= _BV (PWM1_DIR);
#endif
#ifdef PWM2
    if (reverse & PWM_REVERSE_BIT (PWM2))
	pwm_ocr_dir_reverse_b |= _BV (PWM2_DIR);
#endif
#ifdef PWM3
    if (reverse & PWM_REVERSE_BIT (PWM3))
	pwm_ocr_dir_reverse_e |= _BV (PWM3_DIR);
#endif
#ifdef PWM4
    if (reverse & PWM_REVERSE_BIT (PWM4))
	pwm_ocr_dir_reverse_e |= _BV (PWM4_DIR);
#endif
}