Need a bit of help verifying if i have done this correctly - Page 1
Forum section: electronics / general

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 28, 2013, 06:42:50 pm
content: Hello, i am brand new to programming AVR's in low level C and i would like to make sure i have done this correctIts going to be a controler for a nixie tube clock which i am using atmel studio 6.1 to program my ATMega329PAMy code is currently the pure basics to set up the pins and pwm line and i would like to know if it is done correctlyCode:/** Nixie_Clock_REV2_0_0.c** Created: 27/06/2013 23:12:18* Author: carbon dude oxide*/#define F_CPU = 20000000UL;#include <avr/io.h>#include <avr/interrupt.h>#include <avr/delay.h>double nixieSupplyDutyCycle = 0;int powerSaveMode = 0x00;int main(void){//i/o declarationDDRA = 0xFF; //set port A i/o (all outputs) Digits 1-2DDRB = 0x80; // set port B i/o outputs:PB7 inputs:PB1-PB6DDRC = 0xFF; //set port A i/o (all outputs) Digits 3-4DDRD = 0xFF; //set port A i/o (all outputs) Digits 5-6DDRE = 0xFF; //set port A i/o (all outputs) Digits 7-8//PWM timer stuff.TCCR2A = (1 << COM2A1) | (1 << WGM21) | (1 << WGM20); // set to fast PWM and mode.TIMSK2 = (1 << TOIE2); // set to run interrupt when timer overflowsOCR2A = ((nixieSupplyDutyCycle/100)*255); //work out duty cyclesei(); //set up external interrupts;TCCR2A |= (1 << CS20); // set pre-scaler to 1. and start timerwhile(1){//TODO:: Please write your application code}}//interupt serviceISR(TIMER2_OVF_vect_num) //interupt for timer 2 - for nixie voltage supply modulation at night or low power mode.{OCR2A = ((nixieSupplyDutyCycle/100)*255); //work out duty cycle as it may change mid code.}Thanks

author: c4757p
author_rank: Super Contributor
timestamp: June 28, 2013, 06:50:50 pm
content: It looks mostly good to me. Three things I'd like to point out:1) You don't need floating point for duty cycle, and if you take it out your code will become much, much smaller. There's no point in converting x/100 to y/255 for the duty cycle. Just store it as a number out of 255 from the beginning.2) Why are you interrupting on timer overflow? Don't you just want to let it free-run for PWM?3) Is this PWM for dimming the tubes? Slow it way down. That high side switch isn't going to run very fast. Try for around 1-5kHz.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 28, 2013, 06:57:19 pm
content: Ok how do i slow it down? (Which register do i hve to ajust a i dont h e the datasheet on me currently and by how much?) And the interupt thing at the bottom is so i can ajust it because duing the day i dont want any pwm but once it reaches x time i would like to modulate to 20% ishWould it be better just to remove the interupt and put that like of code in te loop and call it once to set pwm on and off?

author: c4757p
author_rank: Super Contributor
timestamp: June 28, 2013, 07:47:15 pm
content: Line 33 (TCCR2A |= (1 << CS20)sets the speed. Page 157 on thedatasheetexplains how that works. Basically, you're dividing the system clock down to a slower clock, with ratio set by CS20..CS22, and that becomes the timer clock.That interrupt is going to fire every single time the timer overflows -every PWM cycle. Pulse... interrupt. Pulse... interrupt. Pulse... interrupt. I don't think that's what you want. Include that check somewhere in your main loop.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 28, 2013, 08:11:15 pm
content: My phone wont let me open the main datasheet as its too bigOtherwise i would have looked for it and found it myself, thanksOk so the double would i just change it to a float and do 1.0f meaning 100% dutycycle and put it to 0.2f for 20% dutycycle correct?

author: c4757p
author_rank: Super Contributor
timestamp: June 28, 2013, 08:38:31 pm
content: No. No float, no double, integer only. The MCU has no floating point support, so as soon as the keywords 'float' or 'double' are anywhere in your file, the compiler has to automatically dump in a software floating point library.Change it to a uint8_t or unsigned char. 255 = 100% duty cycle and 50 is close enough to 20% duty cycle.

author: Jeroen3
author_rank: Super Contributor
timestamp: June 28, 2013, 09:16:29 pm
content: Your "nixieSupplyDutyCycle" variable is not "volatile" and it is used in ISR (interrupt service routines).The optimizer might implicitly convert it to a "const" if you'r not changing it random enough or put "volatile" in front of the declaration.Volatile basically means, "always store this variable in sram".Take a look a this faq, often referred to by avrfreaks people.http://www.nongnu.org/avr-libc/user-manual/FAQ.html

author: westfw
author_rank: Super Contributor
timestamp: June 29, 2013, 08:10:51 am
content: Code:#define F_CPU = 20000000UL;That's seriously wrong. You probably want:Code:#define F_CPU 20000000UL#define creates a text substitution at the pre-processor level; if there were a calculation later that looked likeCode:ticks = F_CPU/256L;(which is not unlikely), it would have expanded toCode:ticks = = 20000000UL;/256L;which is unlikely to compile, much less do the right thing.Normally, F_CPU would get set by the build process as a result of the definition of the target system, rather than being hardcoded into the C code.

author: Jeroen3
author_rank: Super Contributor
timestamp: June 29, 2013, 11:07:42 am
content: Usually F_CPU is derived from the multiplier and divider settings.The only "known" is the XTAL.But with atmega I don't think that is possible.

author: Bored@Work
author_rank: Super Contributor
timestamp: June 29, 2013, 11:16:49 am
content: F_CPU is what has become the de-facto standard for defining an AVR's clock frequency. XTAL is just used by a few older libraries. You can now start to argue that XTAL might make more sense, but as it happened, F_CPU won the race years ago.

author: c4757p
author_rank: Super Contributor
timestamp: June 29, 2013, 11:27:49 am
content: You know, back when I did a lot more programming I'd have immediately caught those things. Guess it's not quite like riding a bicycle...

author: mrflibble
author_rank: Super Contributor
timestamp: June 29, 2013, 12:03:49 pm
content: Quote from: c4757p on June 29, 2013, 11:27:49 amhttp://dilbert.com/2013-06-28/

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 29, 2013, 01:28:59 pm
content: I did notice then when i built it it came up with some errors for the delay stuff when it was using F_CPUthat would be why.How would i use I2C with it? Specificaly so i can read and write the time to a ds1307 rtcAfter further looking into the datasheet tere is no mention of I2C but there is reffereance to usi, is that the I2C section?

author: Bored@Work
author_rank: Super Contributor
timestamp: June 29, 2013, 03:56:48 pm
content: What, you picket an AVR with USI? Normally one would only do this if it is needed for backward compatibility with some old AVR code. USI is a bastard that has been replaced on later AVR designs by a proper I2C (aka TWI) module and a feature-enhanced USART.There should be one or a few Atmel application notes describing how to use USI for a kind of I2C master. IIRC you won't get a 100% compatible I2C master out of USI, so you have to hope that your I2C slaves aren't too picky.

author: AlfBaz
author_rank: Super Contributor
timestamp: June 29, 2013, 04:05:52 pm
content: Quote from: c4757p on June 29, 2013, 11:27:49 amI hadn't done any c for about a year. Got on the other day and forgot how to declare a pointer

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 29, 2013, 05:18:04 pm
content: Ok in the datasheet location: 20.5.3The register USICR contains the bits USIWM1 and USIWM0 which sets the usi to three wire mode or two wire mode, there are two modes of two wire mode and i am unsure which one to use(page 200)The two modes are:Code:Two-wire mode. Uses SDA (DI) and SCL (USCK) pins(1).The Serial Data (SDA) and the Serial Clock (SCL) pins are bi- directional and uses open-collector output drives. The output drivers are enabled by setting the corresponding bit for SDA and SCL in the DDR Register.When the output driver is enabled for the SDA pin, the output driver will force the line SDA low if the output of the Shift Register or the corresponding bit in the PORT Register is zero. Otherwise the SDA line will not be driven (i.e., it is released). When the SCL pin output driver is enabled the SCL line will be forced low if the corresponding bit in the PORT Register is zero, or by the start detector. Otherwise the SCL line will not be driven.The SCL line is held low when a start detector detects a start condition and the output is enabled. Clearing the Start Condition Flag (USISIF) releases the line. The SDA and SCL pin inputs is not affected by enabling this mode. Pull-ups on the SDA and SCL port pin are disabled in Two-wire mode.AndCode:Two-wire mode. Uses SDA and SCL pins.Same operation as for the Two-wire mode described above, except that the SCL line is also held low when a counter overflow occurs, and is held low until the Counter Overflow Flag (USIOIF) is cleared.Which one should i use?I think its the 2nd choice but im unsure

author: Bored@Work
author_rank: Super Contributor
timestamp: June 29, 2013, 05:19:40 pm
content: Get the relevant application note. The datasheet doesn't tell you how to use that USI shit, just that it exists.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 29, 2013, 09:30:38 pm
content: this is the application note to use the USI as an I2C master, i understand how it should work but i am unsure how to put it in codeclick me i am the application notehow i believe it works after reading about:you have a data line and clock line, when the bus is idle both lines are pulled high from the two pull-up resistors at the end of the line.only the master can initiate a data transfer and it does this by pulling SDA low while SCL is high which is the start command.the SCL then starts pulsing at a set rate for example 100kHz as its max value but it can be slower, the SDA line can only transistion between high and low when the SCL line is low.the first transmistion is an 8 bit address where the first 7 are an address for a slave on the bus and the 8th bit is the read/write bit depending on what the master wants to do to the slave. a 9th bit is then sent as low from the slave to acknowledge the bit transfer, if this bit is high then no slave acknowledged so the master does a stop command where it lets SDA go high when SCL is also high which is the stop command.if the address is acknowledged then the master can proceed to send data to the slave, first the bit for the register the slave is meant to read/write to then an acknowledgement bit is returned and then the data for that register is sent followed by a returned acknowledgement. this is repeated for x amount of times depending on how much data/registers need to be changed. once the final byte of data is sent then the master initiates a stop command where it lets SDA go high when SCL is high and thats the end.when the master wants to read from the slave it initiates the first byte with the write bit and then sends the byte register it wants waits for acknowledgement and then receives the data then followed by the acknowledgement bit.if any acknowledgement bit is high the master initiates the stop command and goes back to being idle.have i got that right?if so how would i code that?

author: Bored@Work
author_rank: Super Contributor
timestamp: June 29, 2013, 10:10:49 pm
content: If you want/need to know how I2C works in detail get the Phillips/NXP specification for I2C.How to code it? You google for code for USI for your compiler. If you find nothing you instead get the code associated with the app note, hidden somewhere on the Atmel web site. You unpack it, and "I bet my bottom dollar" that you'll find rather rubbish C code, written for some rare AVR C compiler. So you start to port that code to the compiler you use.The I2C spec comes in handy when trying to get it right. One of those cheap logic analyzers or an oscilloscope also comes in handy when you have to debug the stuff.And in the end you wish you selected an AVR with a real I2C/TWI module instead of USI.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 29, 2013, 10:16:21 pm
content: i have not bought the IC yet so i can find one with true I2C on it

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 29, 2013, 11:07:26 pm
content: ok i have looked into it a bit further and have found the ATMega128A which meets my requirements AND has a I2C interfaceits also got a nice section in the data sheet explaining how to do it with code samples, im going to try and figure it out but i will return

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 29, 2013, 11:56:16 pm
content: ok here is what i belive should eb the code to write a packet of data to a ic with an id of 0x68 (DS1307 RTC ic)Code:* Created: 27/06/2013 23:12:18* Author: carbon dude oxide*/#define F_CPU 20000000UL#define DS1307Address 0x68#define SLA_W 0x69#define SLA_R 0x68#define DATA 0x00#include <avr/io.h>#include <util/twi.h>int main(void){while(1){//TODO:: Please write your application code}}void writeTimeToDS1307(void){TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); //enable I2C and request start commandwhile (!(TWCR & (1 << TWINT))); //wait until start command has been sent//some form of bit check / verificationTWDR = SLA_W; //load address and read/write bit into TWDR registerTWCR = (1 << TWINT) | (1 << TWEN); // clear TWINT and send data packetwhile (!(TWCR & (1 << TWINT))); //wait until acknowledge is received//some form of bit check / verificationTWDR = DATA //load data to be sent into TWDR registerTWCR = (1 << TWINT) | (1 << TWEN); // clear TWINT and send data packetwhile (!(TWCR & (1 << TWINT))); //wait until acknowledge is received//some form of bit check / verificationTWCR = (1 << TWINT) | (1<< TWEN) | (1 << TWSTO); //send stop command}

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: June 30, 2013, 05:18:01 pm
content: ok a bit of an update from the last code, is this function correct to update the time on the DS1307 RTC IC from an ATMega128A? also what do i put in the ERROR function?Code:/** NixieTubeClockRev2_0_1.c** Created: 30/06/2013 16:58:31* Author: carbon dude oxide*/#define F_CPU 20000000UL#define DS1307_R 0xd0#define DS1307_W 0xd1#define second 0x00; //current seconds 0-3 bit is second 4-6 bit is 10 second 7 is low#define minute 0x00; //current minutes 0-3 bit is minute 4-6 bit is 10 minute 7 is low#define hour 0x00; //current hours 0-3 bit is hour 4-5 bit is 10 hour 6 bit is 12/24 hour 7 is low#define day 0x00; //current day 0-2 bit is day 3-7 is low#define date 0x00; //current date 0-3 bit is date 4-5 is 10 date 6-7 is low#define month 0x00; //current month 0-3 bit is month 4 bit is 10 month 5-7 is low#define year 0x00; // current year 0-3 bit is year 4-7 bit is 10 year#define second_W 0x00; //temporary second to write#define minute_W 0x00; //temporary minute to write#define hour_W 0x00; //temporary hour to write#define day_W 0x00; //temporary day to write#define date_W 0x00; //temporary date to write#define month_W 0x00; //temporary month to write#define year_W 0x00; //temporary year to write#define control_W 0x10; //set RTC to output a 1Hz pulse on SQW/OUT pin to write#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include <util/twi.h>int main(void){TWBR = 0x17; // set bit rate register to 23 to reduce the SCL to 100KHzwhile(1){//TODO:: Please write your application code}}void ERROR(){//TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);}void updateTime(){TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); //send start conditionwhile (!(TWCR & (1 << TWINT))); //wait until start condition has been sentif ((TWCR & 0xF8) != TW_START) ERROR(); //if the status register is not TW_START go to errorTWDR = DS1307_W; //load write address into registerTWCR = (1 << TWINT) | (1 << TWEN); //send address data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = second_W; //load seconds to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = minute_W; //load minutes to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = hour_W; //load hours to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = day_W; //load days to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = date_W; //load date to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = month_W; //load months to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = year_W; //load years to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWDR = control_W; //load control data to be writtenTWCR = (1 << TWINT) | (1 << TWEN); //send data packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((TWSR & 0xF8) != TW_MT_SLA_ACK) ERROR(); //has data packet been acknowledged? if not goto error.TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // initiate stop command}EDIT: noticed an error when defining (put = there)2nd EDIT: added TWBR to set the clock frequency to 100KHz using 20MHz CPU clock speed3rd EDIT: missed some ; on some of the lines