From 41d36f28612cb6c49cf0260236f3b834549883be Mon Sep 17 00:00:00 2001 From: Leo C Date: Tue, 14 Oct 2014 12:53:48 +0200 Subject: pin command, add user configurable i/o pins --- avr/pin.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 avr/pin.c (limited to 'avr/pin.c') diff --git a/avr/pin.c b/avr/pin.c new file mode 100644 index 0000000..8a9f000 --- /dev/null +++ b/avr/pin.c @@ -0,0 +1,372 @@ +#include "common.h" +#include +#include +#include "debug.h" +#include "pin.h" + + +/* + +Pin Name Port Timer Mode max div max div min f [Hz] +---------------------------------------------------------------------------------- +0 PG5 OC0B PWM (2**8)*1024 262144 70.31 +1 PG4 +2 CLK2 PB4 OC2A Toggle (2**8)*1024*2 524288 35.16 +3 CLOCK PB5 OC1A PWM (2**16)*1024 67108864 0.2746 +4 PB6 OC1B PWM (2**16)*1024 67108864 0.2746 +5 PB7 OC0A Toggle (2**8)*1024*2 524288 35.16 +6 PG3 +7 PG2 +8 PG1 +9 PG0 +10 CLKO PE7 + + +pre Timer0 Timer1 Timer2 +-------------------------------------------------- +0 0 0 0 +1 1 1 1 +2 8 x8 8 x8 8 x8 +3 64 x8 64 x8 32 x4 +4 256 x4 256 x4 64 x2 +5 1024 x4 1024 x4 128 x2 +6 256 x2 +7 1024 x4 +-------------------------------------------------- + +*/ + + +#define PWMTOGGLE 0b01 +#define PWMPOS 0b10 +#define PWMNEG 0b11 + + +const FLASH uint8_t prescale_factors_01[] = + { 8, 8, 4, 4, 0 }; + +const FLASH uint8_t prescale_factors_2[] = + { 8, 4, 2, 2, 2, 4, 0 }; + +typedef volatile struct { + uint8_t pin; + uint8_t ddr; + uint8_t pout; +} port_t ; + +struct pindef_s { + port_t * const adr; + const uint8_t mask; +#define NO_TIMER 0 +#define TIMER0 (1 << 0) +#define TIMER1 (2 << 0) +#define TIMER2 (3 << 0) +#define TIMER (3 << 0) +#define T_16BIT (1 << 3) +#define CHANA (1 << 4) +#define CHANB (0 << 4) + const uint8_t timer; +}; + + +const FLASH struct pindef_s pinlist[PIN_MAX] = { + { (port_t *) &PING, _BV(5), TIMER0 | CHANB }, + { (port_t *) &PING, _BV(4), NO_TIMER }, + { (port_t *) &PINB, _BV(4), TIMER2 | CHANA }, + { (port_t *) &PINB, _BV(5), TIMER1 | CHANA | T_16BIT }, + { (port_t *) &PINB, _BV(6), TIMER1 | CHANB | T_16BIT }, + { (port_t *) &PINB, _BV(7), TIMER0 | CHANA }, + { (port_t *) &PING, _BV(3), NO_TIMER }, + { (port_t *) &PING, _BV(2), NO_TIMER }, + { (port_t *) &PING, _BV(1), NO_TIMER }, + { (port_t *) &PING, _BV(0), NO_TIMER }, + { (port_t *) &PINE, _BV(7), NO_TIMER }, +}; + +void pin_timer_off(uint8_t timertype) +{ + uint8_t chan_mask; + + if (timertype & CHANA) + chan_mask = 0xc0; + else + chan_mask = 0x30; + + switch (timertype & TIMER) { + case TIMER0: + if (TCCR0A & chan_mask) { + TCCR0B = 0; + TCCR0A = 0; + PRR0 |= _BV(PRTIM0); + } + break; + case TIMER1: + if (TCCR1A & chan_mask) { + TCCR1B = 0; + TCCR1A = 0; + PRR0 |= _BV(PRTIM1); + } + break; + case TIMER2: + if (TCCR2A & chan_mask) { + TCCR2B = 0; + TCCR2A = 0; + PRR0 |= _BV(PRTIM2); + } + break; + } +} + +int pin_config(int pin, pinmode_t mode) +{ + if ((unsigned) pin >= ARRAY_SIZE(pinlist)) { + /* Invalid pin number */ + return -1; + } else { + + port_t *p = pinlist[pin].adr; + uint8_t bit = pinlist[pin].mask; + + switch (mode) { + case INPUT: + pin_timer_off(pinlist[pin].timer); + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + p->ddr &= ~bit; + p->pout &= ~bit; + } + break; + case INPUT_PULLUP: + pin_timer_off(pinlist[pin].timer); + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + p->ddr &= ~bit; + p->pout |= bit; + } + break; + case OUTPUT: + pin_timer_off(pinlist[pin].timer); + case OUTPUT_TIMER: + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + p->ddr |= bit; + } + break; + default: + /* Invalid pin mode */ + return -1; + } + } + return 0; +} + +void pin_write(int pin, uint8_t val) +{ + port_t *p = pinlist[pin].adr; + uint8_t bit = pinlist[pin].mask; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (val) + p->pout |= bit; + else + p->pout &= ~bit; + } +} + +int pin_read(int pin) +{ + port_t *p = pinlist[pin].adr; + uint8_t bit = pinlist[pin].mask; + + return (p->pin & bit) != 0; +} + +pinmode_t pin_config_get(int pin) +{ + uint8_t timertype = pinlist[pin].timer; + + if (timertype & TIMER) { + + uint8_t chan_mask; + if (timertype & CHANA) + chan_mask = 0xc0; + else + chan_mask = 0x30; + + switch (timertype & TIMER) { + case TIMER0: + if (TCCR0A & chan_mask) + return OUTPUT_TIMER; + break; + case TIMER1: + if (TCCR1A & chan_mask) + return OUTPUT_TIMER; + break; + case TIMER2: + if (TCCR2A & chan_mask) + return OUTPUT_TIMER; + break; + } + } + + port_t *p = pinlist[pin].adr; + uint8_t bit = pinlist[pin].mask; + + if (p->ddr & bit) + return OUTPUT; + + if (p->pout & bit) + return INPUT_PULLUP; + + return INPUT; +} + +/* + * return -1: pin has no timer output + * 0: pin is not configured for timer output + * > 0: divider + */ + +long pin_clockdiv_get(int pin) +{ + long divider; + uint8_t prescale; + const FLASH uint8_t *pstab; + + uint8_t timertype = pinlist[pin].timer; + if ((timertype & TIMER) == 0) + return -1; + + if (pin_config_get(pin) != OUTPUT_TIMER) + return 0; + + switch (timertype & TIMER) { + case TIMER0: + prescale = TCCR0B; + divider = OCR0A; + break; + + case TIMER1: + prescale = TCCR1B; + divider = ICR1; + break; + + case TIMER2: + prescale = TCCR2B; + divider = OCR2A; + break; + } + + prescale = (prescale & 0x07) - 1; + divider += 1; + + pstab = (timertype & TIMER) == TIMER2 ? + prescale_factors_2 : prescale_factors_01; + + while (prescale--) + divider *= pstab[prescale]; + + if ((timertype & (CHANA|T_16BIT)) == CHANA) + divider *= 2; + + return divider; +} + +int pin_clockdiv_set(int pin, unsigned long divider) +{ + unsigned long ltop; + uint16_t top; + uint8_t prescale; + const FLASH uint8_t *pstab; + + uint8_t timertype = pinlist[pin].timer; + if ((timertype & TIMER) == 0) + return 0; + + if (divider < 2) + return -1; + + ltop = divider; + if ((timertype & (CHANA|T_16BIT)) == CHANA) + ltop /= 2; + + if (ltop > 1024 * ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) + return -1; + + prescale = 1; + pstab = (timertype & TIMER) == TIMER2 ? + prescale_factors_2 : prescale_factors_01; + +// debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d\n", +// pin, ltop, prescale); + + while (ltop > ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) { +// debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d, *pstab %d\n", +// pin, ltop, prescale, *pstab); + + if (*pstab == 0) + return -1; + ltop /= *pstab++; + prescale++; + } + + if (ltop == 0) + return -1; + + top = ltop - 1; + + PING |= _BV(0); /* Debug */ + + switch (timertype & TIMER) { + case TIMER0: + PRR0 &= ~_BV(PRTIM0); + TCCR0B = (1 << WGM02); + TCNT0 = 0; + OCR0A = top; + if (timertype & CHANA) { + TCCR0A = (PWMTOGGLE << COM0A0) | (0b11 << WGM00); + } else { + OCR0B = top/2; + TCCR0A = (PWMPOS << COM0B0) | (0b11 << WGM10); + } + TCCR0B = (1 << WGM02) | (prescale << CS10); + break; + + case TIMER1: + PRR0 &= ~_BV(PRTIM1); + TCCR1B = (0b11 << WGM12); + TCNT1 = 0; + ICR1 = top; + if (timertype & CHANA) { + OCR1A = top/2; + TCCR1A = (PWMPOS << COM1A0) | (0b10 << WGM10); + } else { + OCR1B = top/2; + TCCR1A = (PWMPOS << COM1B0) | (0b10 << WGM10); + } +// debug("pin: %d, top: %u," +// " ICR1: %u, OCR1A: %u, OCR1B: %u\n", +// pin, top, ICR1, OCR1A, OCR1B); + + TCCR1B = (0b11 << WGM12) | (prescale << CS10); + break; + + case TIMER2: + PRR0 &= ~_BV(PRTIM2); + TCCR2B = (1 << WGM22); + TCNT2 = 0; + OCR2A = top; + if (timertype & CHANA) { + TCCR2A = (PWMTOGGLE << COM2A0) | (0b11 << WGM20); + } else { + OCR2B = top/2; + TCCR2A = (PWMPOS << COM2B0) | (0b11 << WGM10); + } + TCCR2B = (1 << WGM22) | (prescale << CS10); + break; + } + + PING |= _BV(0); /* Debug */ + + pin_config(pin, OUTPUT_TIMER); + + return 0; +} + -- cgit v1.2.3 From cd5ee5442821f283f6661befb24b885d944e4c5e Mon Sep 17 00:00:00 2001 From: Leo C Date: Tue, 14 Oct 2014 22:38:56 +0200 Subject: pin command: pin groups work now --- avr/Tupfile | 2 +- avr/cmd_boot.c | 163 ++++++++++++++++++++++++++++++++++++++++-------------- avr/command_tbl.c | 12 ++-- avr/getopt-min.c | 2 +- avr/main.c | 2 +- avr/pin.c | 2 +- include/pin.h | 2 +- 7 files changed, 134 insertions(+), 51 deletions(-) (limited to 'avr/pin.c') diff --git a/avr/Tupfile b/avr/Tupfile index cbceff8..1300a4a 100644 --- a/avr/Tupfile +++ b/avr/Tupfile @@ -1,6 +1,6 @@ include_rules -PROG = stamp-test +PROG = stamp-monitor SRC = main.c SRC += cli.c cli_readline.c command.c command_tbl.c SRC += cmd_help.c cmd_echo.c cmd_date.c cmd_mem.c cmd_boot.c diff --git a/avr/cmd_boot.c b/avr/cmd_boot.c index 285dd65..26855f1 100644 --- a/avr/cmd_boot.c +++ b/avr/cmd_boot.c @@ -278,6 +278,7 @@ static void print_blanks(uint_fast8_t count) } static const FLASH char * const FLASH pinconf_str[] = { + FSTR("?"), FSTR("Input"), FSTR("Pullup"), FSTR("Output"), @@ -327,25 +328,74 @@ int print_pin(int pin, int multi) return 0; } +int pinarg_insert(int pin, int count, int pinarg[]) +{ + int pos; -/* - * TODO: - pin groups - * - error if pin "config clock" on pins without clock - * - stat for single pin (group) - */ - -command_ret_t do_pin(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) + if (pin < 0 || pin >= PIN_MAX) + return -1; + + for (pos = 0; pos < count; pos++) { + if (pin == pinarg[pos]) + return 0; + if (pin < pinarg[pos]) + break; + } + for (int i = count-1; i == pos ; i--) + pinarg[i+1] = pinarg[i]; + pinarg[pos] = pin; + + return 1; +} + +int pinarg_get(char * arg, int pinarg[]) { - int opt, pin; - unsigned long value; + int count = 0; char *endp; + int pin1, pin2, rc; + + while (1) { + pin1 = (int) strtoul(arg, &endp, 10); + if (endp != arg && *endp == '-') { + arg = endp+1; + pin2 = (int) strtoul(arg, &endp, 10); + if (pin1 < pin2) + for (; pin1 < pin2; pin1++) + if ((rc = pinarg_insert(pin1, count, pinarg)) >= 0) + count += rc; + else + return 0; + else + return 0; + } + if (endp != arg && pin1 >= 0) { + if ((*endp == ',' || *endp == '\0') && + (rc = pinarg_insert(pin1, count, pinarg)) >= 0) { + count += rc; + if (*endp == '\0') + return count; + } else + return 0; + } else + return 0; + + arg = endp+1; + } +} + + +command_ret_t do_pin(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ char printheader = 1; + int pinarg[PIN_MAX]; + int pinargc; (void) cmdtp; (void) flag; /* reset getopt() */ optind = 1; + int opt; while ((opt = getopt(argc, argv, PSTR("s"))) != -1) { switch (opt) { case 's': @@ -356,45 +406,59 @@ command_ret_t do_pin(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) } } -// if ((argc - optind) % 2 != 0) -// return CMD_RET_USAGE; - - debug("argc: %d, optind: %d\n", argc, optind); + /* remaining arguments */ + argc -= optind; + + if (argc == 0) + /* print cofig of all pins */ + for (pinargc = 0; pinargc < PIN_MAX; pinargc++) + pinarg[pinargc] = pinargc; + else { + /* get first arg */ + pinargc = pinarg_get(argv[optind++], pinarg); + if (pinargc == 0) + return CMD_RET_USAGE; + else + argc--; + } - switch (argc - optind) { - case 0: - if (printheader) - printf_P(PSTR("Pin Config Level Divider Frequency/Hz\n" - "-----------------------------------------\n")); - for (pin = 0; pin < PIN_MAX; pin++) - print_pin(pin, 1); - - return CMD_RET_SUCCESS; - break; - case 1: - pin = strtol(argv[optind], &endp, 10); - print_pin(pin, 0); + if (argc == 0) { + /* no more args, print config */ + if (pinargc == 1) + print_pin(pinarg[0], 0); + else { + if (printheader) + printf_P(PSTR("Pin Config Level Divider Frequency/Hz\n" + "-----------------------------------------\n")); + for (int i = 0; i < pinargc; i++) + print_pin(pinarg[i], 1); + } return CMD_RET_SUCCESS; - break; } - while (optind < argc ) { + /* arguments must be in pairs: pins conf */ + if (argc % 2 != 1) + return CMD_RET_USAGE; + + while (argc > 0) { + char *endp; + pinmode_t mode = NONE; + int level = 0; + unsigned long value = 0; uint8_t hz_flag = 0; - pin = strtol(argv[optind++], &endp, 10); - switch (toupper(argv[optind][0])) { - case 'L': case 'H': - pin_write(pin, toupper(argv[optind][0]) == 'H'); - pin_config(pin, OUTPUT); + level = 1; + case 'L': + mode = OUTPUT; break; case 'P': - pin_config(pin, INPUT_PULLUP); + mode = INPUT_PULLUP; break; case 'I': case 'T': - pin_config(pin, INPUT); + mode = INPUT; break; default: @@ -429,19 +493,38 @@ command_ret_t do_pin(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) } value = F_CPU/value; } + mode = OUTPUT_TIMER; + } - debug("** setting pin '%d' to '%lu'\n", pin, value); - if (pin_clockdiv_set(pin, value) < 0) { - printf_P(PSTR("Setting pin %d to %lu failed.\n"), - pin, value); + if (mode == NONE) + return CMD_RET_USAGE; + + for (int i = 0; i < pinargc; i++) { + switch (mode) { + case OUTPUT: + pin_write(pinarg[i], level); + /* fall thru */ + case INPUT: + case INPUT_PULLUP: + pin_config(pinarg[i], mode); + break; + case OUTPUT_TIMER: + if (pin_clockdiv_set(pinarg[i], value) < 0) { + printf_P(PSTR("Setting pin %d to %lu failed.\n"), + pinarg[i], value); + } + break; + default: + break; } } optind++; + pinargc = pinarg_get(argv[optind++], pinarg); + argc -= 2; } - return CMD_RET_SUCCESS; } diff --git a/avr/command_tbl.c b/avr/command_tbl.c index 71e70ea..e8af931 100644 --- a/avr/command_tbl.c +++ b/avr/command_tbl.c @@ -148,20 +148,20 @@ CMD_TBL_ITEM( CMD_TBL_ITEM( pin, CONFIG_SYS_MAXARGS, 0, do_pin, - "Set or get pin state", - "[-s][pins]\n" + "Set or query pin state", + "[-s] []\n" " - print cofiguration and state or frequency of pins\n" " print all pins, if argument is omitted\n" - "pin pins [h[igh]]|[l[ow]]\n" + "pin h[igh]|l[ow]\n" " - config pins as output and set to level high or low\n" - "pin pins [ts]|[i[n]]|[p[ullup]]\n" + "pin ts|i[n]|p[ullup]\n" " - config pins as input/tristate or input with pullup\n" - "pin pins value[K|M][Hz]\n" + "pin value[K|M][Hz]\n" " - output a clock on pins\n" " value is system clock divider or frequency, if 'Hz' is appended\n" " divider is rounded down to next possible value (depends on pin)\n" "\n" - "pins is a comma separated list of numbers or ranges, i.e. \"0,9,3-6\"\n" + " is a comma separated list of numbers or ranges, i.e. \"0,9,3-6\"\n" ), CMD_TBL_ITEM( diff --git a/avr/getopt-min.c b/avr/getopt-min.c index 778258d..571c14f 100644 --- a/avr/getopt-min.c +++ b/avr/getopt-min.c @@ -39,7 +39,7 @@ getopt( /* returns letter, '?', EOF */ || argv[optind][0] != '-' /* no more options */ || argv[optind][1] == '\0' /* not option; stdin */ ) - return EOF; + return -1; } c = argv[optind][sp]; /* option letter */ diff --git a/avr/main.c b/avr/main.c index 0470fea..e7ebd5d 100644 --- a/avr/main.c +++ b/avr/main.c @@ -228,7 +228,7 @@ int main(void) i2c_init(CONFIG_SYS_I2C_CLOCK); #endif - printf_P(PSTR("\n(ATMEGA1281+HD64180)_stamp Tester\n")); + printf_P(PSTR("\nATMEGA1281+Z8S180 Stamp Monitor\n\n")); main_loop(); diff --git a/avr/pin.c b/avr/pin.c index 8a9f000..14896b3 100644 --- a/avr/pin.c +++ b/avr/pin.c @@ -12,7 +12,7 @@ Pin Name Port Timer Mode max div max div min f [Hz] 0 PG5 OC0B PWM (2**8)*1024 262144 70.31 1 PG4 2 CLK2 PB4 OC2A Toggle (2**8)*1024*2 524288 35.16 -3 CLOCK PB5 OC1A PWM (2**16)*1024 67108864 0.2746 +3 ZCLK PB5 OC1A PWM (2**16)*1024 67108864 0.2746 4 PB6 OC1B PWM (2**16)*1024 67108864 0.2746 5 PB7 OC0A Toggle (2**8)*1024*2 524288 35.16 6 PG3 diff --git a/include/pin.h b/include/pin.h index e673b56..5b37587 100644 --- a/include/pin.h +++ b/include/pin.h @@ -4,7 +4,7 @@ /* Number of user configurable I/O pins */ #define PIN_MAX 11 -typedef enum {INPUT, INPUT_PULLUP, OUTPUT, OUTPUT_TIMER} pinmode_t; +typedef enum {NONE, INPUT, INPUT_PULLUP, OUTPUT, OUTPUT_TIMER} pinmode_t; int pin_config(int pin, pinmode_t mode); pinmode_t pin_config_get(int pin); -- cgit v1.2.3