Why I am always getting Zero PWM output? - avr

I want to output two different analog values with 10 bit resolution i.e. dac_value ranging from 0-1023. I am using ATmega16 with external crystal 4MHz. I have also tried connecting RC filter at output but nothing changed.
I am constantly getting zero output, can someone help ??
#include <avr/io.h>
#include <avr/interrupt.h>
void initPWM()
{
TCCR1A |= (1<<WGM11) | (1<<WGM10) | (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) ;
TCCR1B |= (1<<WGM12) | (1<<CS10);
}
uint16_t dac_value1 = 100, dac_value2 = 200;
int main(void)
{
initPWM();
while(1) {
OCR1A = dac_value1;
OCR1B = dac_value2;
}
for (;;) {}
}

You are assigning wrong bits to wrong registers.
Just to clarify: PWM it is NOT a analog output. It is quickly changed high or low output state. PWM value determines how long output will be in each state (high or low) withing timer period.
If you want to make "analog" output, you need to filter output signal, for example, passing it thru a RC-filter, also you need to make output as fast as possible, that's mean you need to select lower prescaler, and choose Fast-PWM mode. In your current configuration, you will get pwm with 1024 prescaler. I.e. less than 4 periods of timer per second.
So, if you we will assign a fast pwm with prescaler 1 (which will give us a 3906 Hz output) with inverted output (i.e. higher OCR1x value leads to lower output value) it will be something like this:
void initPWM()
{
TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0);
// here bits WGM10 WGM11 (with WGM12 in TCCR1B) will select the Fast PWM mode with 10 bit resolution;
// COM1A1 COM1A0 COM1B1 COM1B0 select an inverted PWM output on pins OC1A OC1B
TCCR1B = (1<<WGM12) | (1<<CS10);
// CS10 will select 1:1 prescaler (i.e. 4000000/1024 timer periods per second)
// Also, your PWM output will not be visible on corresponding pins, unless you will configure an DDR bits for these pins to output. Those pins are PD5 and PD4 on ATmega16
DDRD |= (1 << 4) | (1 << 5);
}
Next thing you need to consider: when your application main() function execution reaches it's end, it will jump to reset vector. So, put an empty loop to the end of main():
int main(void)
{
uint16_t dac_value1, dac_value2;
dac_value1 = 123; // do not forget to init variables!!!
dac_value2 = 987;
initPWM();
OCR1A = dac_value1;
OCR1B = dac_value2;
for(;;) {
// main loop. Now it's empty
}
}

Related

how to flush write to raspberry pi gpio pins

I have a program written in C++ on a Raspberry Pi 3. I mmap /dev/gpiomem to access the GPIO registers directly in user mode. Here is my function to write to output pins:
static uint32_t volatile *gpiopage; // initialized by mmap() of /dev/gpiomem
static uint32_t lastretries = 0;
void PhysLib::writegpio (uint32_t value)
{
uint32_t mask = 0xFFF000; // [11:00] input pins
// [23:12] output pins
// all the pins go through an inverter converting the 3.3V to 5V
gpiopage[GPIO_CLR0] = value;
gpiopage[GPIO_SET0] = ~ value;
// sometimes takes 1 or 2 retries to make sure signal gets out
uint32_t retries = 0;
while (true) {
uint32_t readback = ~ gpiopage[GPIO_LEV0];
uint32_t diff = (readback ^ value) & mask;
if (diff == 0) break;
if (++ retries > 1000) {
fprintf (stderr, "PhysLib::writegpio: wrote %08X mask %08X, readback %08X diff %08X\n",
value, mask, readback, diff);
abort ();
}
}
if (lastretries < retries) {
lastretries = retries;
printf ("PhysLib::writegpio: retries %u\n", retries);
}
}
Apparently there is some internal cache or something delaying the actual updating of the pins. So I'm wondering if there is some magic MRC or MCR or whatever that I can put so I don't have to read the pins to wait for the update to actually occur.
I'm quite sure this is happening because this code is part of a loop:
while (true) {
writegpio (0x800000); // set gpio pin 23
software timing loop for 1uS
writegpio (0); // clear gpio pin 23
software timing loop for 1uS
}
Sometimes Linux timeslices during the software timing loops on me and I get a delay longer than 1uS, which is ok for this project. Before I put in the code that loops until it reads the updated bit, sometimes the voltage on the pin stays high for longer than 1uS and is then low for correspondingly less than 1uS, or vice versa, implying that there is a total of 2uS delay for the two timing loops but the update of the actual pin is being delayed by the hardware. After inserting the corrective code, I always get at least 1uS of high voltage and 1uS of low voltage each time through the loop.

how use the MPU 6050 in ultra low power mode

I'm currently trying to set up a fermentation specific gravity monitor, using a tilt sensor. The process can take several weeks, and must be contained in a sterile container, so must be battery powerered. I'm using a slightly modified ESP8266-01, which enters sleep mode then wakes once an hour to take a measurement, transmit the data, and return to sleep mode. I'm using an MPU6050 for the tilt sensor. Firstly, I can't seem to put the mpu into sleep mode when the esp is off, it always seems to take around 4mA, and secondly, I only need one axis, is it possible to disable everything else to limit power consumption further? I can't seem to find anything in the manual to disable axis, only to calibrate them. my code is below
experimenting with the registers below seem to make no difference, adding them, taking them out altogether, still takes around 4mA. Tried setting to 1 to put the mpu to sleep at the end of the cycle but makes no difference.
Wire.write(0x6B);
Wire.write(0);
I'm very new to this and im struggling to interpret the manual when it refers to bit6 in addr 6b, how do i set bit 6?
If i could restict the mpu to only 1 axis, no acceleration, and to deep sleep inbetween measurements I should be able to get the power consumption around 0.5mA which gives me agood battery life using a single 18650. Any advice would be greatly appreciated!
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "MPU6050.h"
#include "I2Cdev.h"
#include "Wire.h"
// Update these with values suitable for your network.
const char* ssid = "****";
const char* password = "******";
IPAddress server(192, 168, 1, 90);
WiFiClient espClient5;
PubSubClient client(espClient5);
long lastMsg = 0;
char msg[50];
const uint8_t scl = 5; //D1
const uint8_t sda = 4; //D2
int val;
int prevVal = 0;
String pubString;
char gravity[50];
MPU6050 mpu;
const int sleepTimeS = 10; //only 10 seconds for testing purposes, set to
1hr when operational
int counter=0;
int16_t ax, ay, az;
int16_t gx, gy, gz;
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) { //not
required in this application
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "test";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("AliveRegister", "FermentMon");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
#define ONE_WIRE_BUS 2 // D4 on physical board
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
float prevTemp = 0;
void setup() {
counter = 0;
Serial.begin(9600);
Wire.begin(0,2);
Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(0); // set to zero wakes teh 6050
Wire.endTransmission(true);
delay(100);
setup_wifi();
client.setServer(server, 1883);
client.setCallback(callback);
if (!client.connected()) {
reconnect();
}
Serial.println("Initialize MPU");
mpu.initialize();
Serial.println(mpu.testConnection() ? "Connected" : "Connection failed");
float temp;
DS18B20.requestTemperatures();
temp = DS18B20.getTempCByIndex(0); // first temperature sensor
char buff[100];
dtostrf(temp, 0, 2, buff);
temp = temp + 0.5;
int tRound = int(temp);
client.publish("Fermenter/temperature", buff);
Serial.print("Fermenter Temperature: ");
Serial.println(temp);
prevTemp = tRound;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
val = map(ax, -17000, 17000, 0, 180);
pubString = String(val);
pubString.toCharArray(gravity, pubString.length() + 1);
client.publish("Fermenter/angle", gravity);
Serial.print("Gravity angle: ");
Serial.println(val);
delay(500);
// counter = counter+1;
Serial.println("sleep mode");
Wire.write(0x6B); //PWR_MGMT_1 register
Wire.write(1); // set to zero wakes teh 6050
// sleep
ESP.deepSleep(sleepTimeS * 1000000);
delay(2000);
}
void loop() {
client.loop();
}
I'm very new to this and im struggling to interpret the manual when it refers to bit6 in addr 6b, how do i set bit 6?
Setting a bit is simple.
Use the follow functions to avoid any brain storming.
// Write register bit
void writeRegisterBit(uint8_t reg, uint8_t pos, bool state)
{
uint8_t value;
value = readRegister8(reg);
if (state)
{
value |= (1 << pos);
}
else
{
value &= ~(1 << pos);
}
writeRegister8(reg, value);
}
// Write 8-bit to register
void writeRegister8(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(MPU_addr);
#if ARDUINO >= 100
Wire.write(reg);
Wire.write(value);
#else
Wire.send(reg);
Wire.send(value);
#endif
Wire.endTransmission();
}
Example Usage: writeRegisterBit(MPU6050_REG_INT_PIN_CFG, 5, 1); //Register 37;Interrupt Latch Enable
For your application:
void acclSetSleepEnabled(bool state)
{
writeRegisterBit(MPU6050_REG_PWR_MGMT_1, 6, state);
}
If i could restict the mpu to only 1 axis, no acceleration, and to deep sleep inbetween measurements I should be able to get the power consumption around 0.5mA which gives me agood battery life using a single 18650
To enter low power accelerometer mode use the following function:
void lowPowerAccel(uint8_t frequency) {
uint8_t value;
value = readRegister8(MPU6050_REG_PWR_MGMT_2);
value &= 0b00111000;
value |= (frequency << 6) | 0b111;
writeRegister8(MPU6050_REG_PWR_MGMT_2, value);
value = readRegister8(MPU6050_REG_PWR_MGMT_1);
value &= 0b10010111;
value |= 0b00111000;
writeRegister8(MPU6050_REG_PWR_MGMT_1, value);
}
This lowPowerAccel function also puts the gyro to standy mode. The function needs a wake up frequency parameter.
This is defined as follows:
/*
* LP_WAKE_CTRL | Wake-up Frequency
* -------------+------------------
* 0 | 1.25 Hz
* 1 | 2.5 Hz
* 2 | 5 Hz
* 3 | 10 H
*/
#define LP_WAKE_CTRL_1_25 0x00
#define LP_WAKE_CTRL_2_5 0x01
#define LP_WAKE_CTRL_5 0x02
#define LP_WAKE_CTRL_10 0x03
I hope, I could answer some of your questions.
Good luck! :)
Are you using a breakout board for the MPU6050? e.g. GY-521. Often they use linear regulators and leds which will consume additional power. It may be necessary to remove these and run the IMU from a direct power source.
Each register in the MPU6050 is 8 bits wide. When setting an individual bit to a desired value you can either use bitwise manipulation (not practical here as we aren't directly interacting with the registers) or directly set all of the bits in the register to the register's new state e.g. 0b00100000 ~ 0x20. Instead of writing a 1 to 0x6B when attempting to put the MPU6050 to sleep you should be writing 0x20.
https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
Referencing page 40-42, if you want to take things a step further you can disable the temperature sensor, accelerometers, and redundant gyroscope axes to save power while the device is active.

Unable to use ADC on atmega328

I am trying to make a proximity sensor using ATmega328P. I am using the onboard ADC to convert the voltage value and if it is higher than the ambient, an LED is lit.
The voltage that is being sensed is according to this circuit:
In the circuit, the VOUT is going to ADC channel 3 and should be sensed (think of the led on the right as the IR Sensor).
When the program starts, it senses 30 readings and takes their average to be used as the ambient setting. If any subsequent measurement is higher than this value, the LED should be lit.
But the LED does not light even if I place my hand above the sensor.
I have tested with just the LED to see if the IR sensor is ok. It is ok by the way.
The code for the microcontroller is as follows:
/*
* Proximity Sensor IR.c
*
* Created: 6/3/2017 2:35:33 PM
* Author : Rishav
*/
#include <avr/io.h>
#include <stdio.h>
#define F_CPU 16000000UL
#include <util/delay.h>
int calibration()
{
unsigned int sum = 0;
for (int i=0; i<30; i++)
{
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
ADCSRA |= (1<<ADIF);
sum += (ADCH<<8)|ADCL;
}
return (sum/30);
}
int main(void)
{
unsigned int val = 0;
ADMUX |= (0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(0<<MUX2)|(1<<MUX1)|(1<<MUX0); //setting the multiplexer to ADC3
ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
DDRB = 0b00000010;
DDRD |= (1<<PCINT22);
PORTD |= (1<<PCINT22);
int calib_value = calibration();
while (1)
{
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
val = (ADCH<<8)|ADCL;
ADCSRA |= (1<<ADIF);
if (val > calib_value)
PORTB = 0b00000010;
}
}
I think there is some problem in the code. Please help.
Some things that come to mind when looking at your code:
You are really not completely initializing the ADMUX and ADCSRA registers - everything you put in there is just 'ORed'-in. (ADLAR in ADMUX is not in a defined state, for example, ADCSRA has even more undefined bits).
After setting the reference voltage source in the ADMUX register, you are supposed to wait for the chip to switch, but don't. Most probably, your first measurement in calibration will be way off. The simplest way to address this is to do one first measurement whose result you simply ignore. (or wait some ms after you have set up ADC).
You are supposed to always read ADCL before ADCH (the AVR locks the ADC for writing further results to the result register when ADCL is read until ADCH is read as well). Your current code has an undefined read order of those 2 registers.
You have to enable the ADC first and select channel and reference voltage afterwards. It is easy to skip this fact in the datasheet.
The ADC is enabled by setting the ADC Enable bit, ADEN in ADCSRA.
Voltage reference and input channel selections will not go into effect
until ADEN is set. Datasheet page 238.
I did not check all of your settings but I am pretty sure that this must be your issue.
Example order:
void init_adc()
{
ADCSRA |= (1<<ADEN); // enable ADC
ADMUX |= (1<<MUX1) | (1<<MUX0); // channel selection ADC3 - PB3
ADMUX &= ~(1<<REFS0); // VCC as reference
ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // setting prescaler to 128
}
As mentioned a you should read ADCL first to:
ADCL must be read first, then ADCH, to ensure that the content of the
Data Registers belongs to the same conversion
I suggest to move this part into a separate function like:
uint16_t read_adc()
{
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
uint8_t adcl = ADCL;
uint8_t adch = ADCH;
ADCSRA |= (1<<ADIF);
return (adch<<8) | adcl;
}

ATtiny84 pwm led flicker

I'm fairly new to avr programming and I'm trying to simply fade 3 leds independently connected to ATtiny84 pwm pins. Right now I have code that should chnage brightness of two different leds. Here is my code:
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRB |= (1 << PB2); // PWM output on PB2
TCCR0A = (1 << COM0A1) | (1 << WGM00); // phase correct PWM mode
OCR0A = 0x10; // initial PWM pulse width
TCCR0B = (1 << CS01); // clock source = CLK/8, start PWM
DDRA |= (1<<PA6); // make OC1A (DDA6) PWM output pin
TCCR1A = (1<<COM1A1) | (1<<COM1B0) | (1<<WGM00); // Clear OC1A/OC1B on Compare Match (bit 7 + 6)
// PWM, Phase Correct
TCCR1B = (1<<CS02); // 256 prescaler
while(1)
{
// change PWM pulse width every 2 seconds
_delay_ms(2000);
OCR0A = 0x10;
OCR1A = 0x10;
_delay_ms(2000);
OCR0A = 0x30;
OCR1A = 0x30;
_delay_ms(2000);
OCR0A = 0x50;
OCR1A = 0x50;
_delay_ms(2000);
OCR0A = 0xA0;
OCR1A = 0xA0;
}
}
The code is mostly copied from internet and I don't really understand the initializations, but now the led connected to PB2 works fine but the one connected to PA6 is not working right. PA6 led does change its brightness accordingly but it also flickers on and off rapidly (about 10 times a second).
First I thought it had something to do with PA6 being also MOSI pin for programming but disconnecting programmer didn't help.
Any help is appreciated! Also any tips for avr programming in general are more than welcome!
Just in case you haven't had a look yet, this is the relevant datasheet for your microcontroller: ATtiny84 Datasheet. Looking at the TCCR* timer configuration registers might reveal something.

ADC reading keeps jumping around on dspic33FJ128MC802, cannot get a stable reading, mplab8.92, xc16 compiler

Good evening,
I'm trying to implement a simple 1 channel ADC reader on a dspic33FJ128MC802, that manually starts sampling data, automatically converts when the sampling is done, and reads and stores data.
This has never been an issue for me, except with this microcontroller, which doesn't seem to have a normal ADC implemented,
I've read through the datasheet section on ADC several times, and I've configured it to my best ability, however the ADC1BUF0 value keeps jumping around inconsistently, between 0 and 4096, when I have a Lab power supply connected directly to the input pins of the ADC.
What I see is the ADC1BUF0 value seems to roughly correspond to the input voltage (0-3.3V), when I pause the debugger, it gives a couple (2-4) readings that are within range +-100 (out of 4096 is not bad).
Then if I continue to run and pause, with the voltage kept the same, the values stored in the buffer suddenly begin to jump +- 500, sometimes even showing 4095 (all 1's) and 0.
Then when I change the lab PSU to a different voltage, it seems to repeat the process of showing me a few correct values, then start to jump around again.
So essentially, it will show me a correct value about 1/2 of the times I pause the debugger.
I don't know what is causing this, I know that I need to run the debugger after changing voltage so that it can clear out the buffers, but something about this microcontroller seems definitely wrong.
Please let me know what can be done to fix this,
The compiler is XC16, IDE is Mplab 8.92
Thanks,
Below is my configuration:
[code]
void InitADC() {
TRISAbits.TRISA0=1;
AD1CON1bits.FORM = 0; // Data Output Format: integer//Signed Fraction (Q15 format)
AD1CON1bits.SSRC = 7; // Interan Counter (SAMC) ends sampling and starts convertion
AD1CON1bits.ASAM = 0; // ADC Sample Control: Sampling begins immediately after conversion
AD1CON1bits.AD12B = 1; // 12-bit ADC operation
AD1CON1bits.SIMSAM =1; // 10-bit ADC operation
AD1CON2bits.CHPS = 0; // Converts CH0
AD1CON2bits.CSCNA = 0; // Do not scan inputs
AD1CON2bits.VCFG = 0; // Use voltage reference Vss/Vdd
AD1CON2bits.ALTS = 0; // Always use input select for channel A
AD1CON2bits.BUFM = 0; // Always start filling at buffer 0
AD1CON3bits.ADRC = 0; // ADC Clock is derived from Systems Clock
AD1CON3bits.SAMC = 0; // Auto Sample Time = 0*Tad
AD1CON3bits.ADCS = 2; // ADC Conversion Clock Tad=Tcy*(ADCS+1)= (1/40M)*3 = 75ns (13.3Mhz)
// ADC Conversion Time for 10-bit Tc=12*Tab = 900ns (1.1MHz)
AD1CON1bits.ADDMABM = 1; // DMA buffers are built in conversion order mode
AD1CON2bits.SMPI = 0; // SMPI must be 0
AD1CON4bits.DMABL = 0; // Only 1 DMA buffer for each analog input
//AD1CHS0/AD1CHS123: A/D Input Select Register
AD1CHS0bits.CH0SA = 0; // MUXA +ve input selection (AIN0) for CH0
AD1CHS0bits.CH0NA = 0; // MUXA -ve input selection (Vref-) for CH0
AD1CHS123bits.CH123SA = 0; // MUXA +ve input selection (AIN0) for CH1
AD1CHS123bits.CH123NA = 0; // MUXA -ve input selection (Vref-) for CH1
IFS0bits.AD1IF = 0; // Clear the A/D interrupt flag bit
IEC0bits.AD1IE = 0; // Do Not Enable A/D interrupt
AD1CSSL = 1; //Scan from AN0 only
AD1PCFGL = 0b111111110; //Only AN0 in analog input mode
AD1CON1bits.ADON = 1; // Turn on the A/D converter
}
int main() {
ADPCFG = 0xFFFE; //make ADC pins all digital except AN0 (RA0)
while(1)
{
AD1CON1bits.SAMP = 1;
while(!AD1CON1bits.DONE);
myVoltage = ADC1BUF0;
}
return 0;
}
[/code]
Seems like I missed a semicolon after while(!AD1CON1bits.DONE)
Without the semicolon it did not wait for the conversion to complete.
I corrected this in the original post, in case someone wants to use the source in this post
Thank you,

Resources