I am writing a c program to send a digital voltage value in bits to DAC (SAM4E) in order to get an analog output.
The program is below:
#include "asf.h"
#include "conf_board.h"
#include "conf_clock.h"
#include "dacc_example.h"
int main (void)
{
sysclk_init();
board_init();
/* Reset DACC registers */
dacc_reset(DACC_BASE);
/* Half word transfer mode */
dacc_set_transfer_mode(DACC_BASE, 0);
dacc_set_timing(DACC_BASE,0, 0x10);
/* Disable TAG and select output channel DACC_CHANNEL */
dacc_set_channel_selection(DACC_BASE, DACC_CHANNEL);
/* Enable output channel DACC_CHANNEL */
dacc_enable_channel(DACC_BASE, DACC_CHANNEL);
/* Set up analog current */
dacc_set_analog_control(DACC_BASE, DACC_ANALOG_CONTROL);
uint32_t dac_val = 0;
while (1)
{
// set voltage to minimum
#define SET_DAC_VAL 0x000
//set voltage to midpoint
// set voltage to 2.7V
//set voltage to 3.2V
//write the conversion value
dacc_write_conversion_data(DACC_BASE, SET_DAC_VAL, DACC_CHANNEL);
}
}
In the above code, I have configured the DAC and in the while loop I am trying to send the digital values to be converted into data conversion register. I want to set the voltages to minimum, midpoint, 2.7V and 3.2V. The maximum voltage is 3.3v and DAC is of 12 bit resolution.
Can anyone help me to set the voltage ??
or
May I know how we represent 2.7 V in 12 bit binary format??
Can anyone help me to set the voltage ??
Setting the voltage is going to depend on how you are interacting with the DAC. Assuming that you are using a microcontroller, then you need to refer to the datasheet. It will describe the registers in the DAC module and how they work. Most of the time there is a code example in there as well.
May I know how we represent 2.7 V in 12 bit binary format??
Your DAC has a 12-bit resolution. This means that you have 12-bits to represent the voltage value that you want to output on the DAC. This means you can represent 2^12 - 1 increments. Assuming that the DAC has a reference voltage of 3.3V, then each increment is 3.3/(2^12 - 1) volts. So if we wanted 2.7V, it would be represented by the value 2.7/(3.3/(2^12 - 1)) = 3350.
Related
I was trying to read analog voltage on pin RC3 on PIC16F15325. I have 3.23V across potentiometer and its output is nearly 1.65V which goes to pin RC3 of PIC microcontroller. For configuration and libraries I used MPLAB Code Cofigurator. Code is as follows:
#include "mcc_generated_files/mcc.h"
void main(void)
{
// initialize the device
SYSTEM_Initialize();
EUSART1_Initialize();
ADC_Initialize();
adc_result_t val1 = 0;
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
// Use the following macros to:
// Enable the Global Interrupts
//INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts
//INTERRUPT_PeripheralInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
// Disable the Peripheral Interrupts
//INTERRUPT_PeripheralInterruptDisable();
while (1)
{
// Add your application code
val1 = ADC_GetConversion(19); // selected channel RC3
printf("Value - %hu \n",val1);
DELAY_milliseconds(1000);
}
}
For voltage I mentioned above, I expected value near "511". Anything beyond 1023 [i.e. (2^10) - 1] is strange as PIC has 10-bit ADC.However uotput I get is:
Kindly, help me solving this issue.
Actually, output was correct but was looking strange only because of format. Just now I looked into datasheet which said about 2 ways of ADC result formatting.
PIC16(L)F15325/45 Datasheet page-228
and inside ADC_Initialize(); ADCON1 = 0x10 which means ADFM is left shift. I just did val1 = val1 >> 6; after ADC_GetConversion(19); and it worked as expected.
I know if I give P0 = 0xFE; it ll make one pin as output and the rest as input.
But what if I want to make just one pin as output or input. Like how we do in PIC Microcontroller:
TRISABITS.TRISA0 = 0; // for setting A0 as output
Please clarify
Pins on the 8051 can't be switched into input or output. There is no direction control. Each pin has a strong driver to GND and a weak driver to VCC. At the rising edge (level change from 0 [GND] to 1 [VCC]) the pin will be driven to VCC a bit stronger for two clock cycles; that is for a better edge.
To use a pin as input you have to set it to 1 and leave it there. A driving source outside the 8051 can now drive the pin to GND, giving a 0 when read. If the external source drives the pin to VCC or let it float, the pin will be read as 1.
To use a pin as output you'll set it to 0 and 1 as you need. Make sure that the driven load has an impedance high enough.
Depending on which compiler you're using. On Keil C you can access the bits just like normal struct fields using the extension for bit-addressable registers
P0.1 = 1; // set P0.1 as input
P0.5 = 0; // set P0.5 as output
Otherwise set a single bit with bitwise operations
char inoutReg = P0;
inoutReg &= ~(1 << n); // clear bit n in P0, i.e. set P0.n as output
inoutReg |= 1 << m; // set bit m in P0, i.e. set P0.m as input
See How do you set, clear, and toggle a single bit?
I'm trying to write a code in C (Using Keil µVision 5, device: AT89C51AC3) that lets me enter 2 Integer numbers, add them and then print them out. The problem is that I'm limited to a byte code size of max. 2048.
My actual code needs 2099 Bytes to run.
Any idea how I could do the same thing using less memory?
#include <stdio.h>
#include <REG52.H>
int main()
{
int a, b;
/*------------------------------------------------
Setup the serial port for 1200 baud at 16MHz.
------------------------------------------------*/
#ifndef MONITOR51
SCON = 0x50; /* SCON: mode 1, 8-bit UART, enable rcvr */
TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */
TH1 = 221; /* TH1: reload value for 1200 baud # 16MHz */
TR1 = 1; /* TR1: timer 1 run */
TI = 1; /* TI: set TI to send first char of UART */
#endif
printf("Enter 2 numbers\n");
scanf("%d%d",&a,&b);
printf("%d\n",a+b);
return 0;
}
You should hiccup when you see this simple code take up 2k+ of memory. That's a lot! The reason for this is that the stdio functions are terribly inefficient.
If you need to save memory and execution speed, you need to code these yourself. Which is not so hard, since you probably just need to read integers and not everything else those function can handle (float numbers, strings etc).
Also get rid of the int type, use the fixed size types from stdint.h instead. (If this is a 8 bit MCU, you should also avoid 16 bit numbers unless they are necessary.)
In addition, you will have to code the I/O part as well. On a microcontroller this would probably mean writing your own UART driver.
You should be able to reduce the code size to a couple of hundred bytes, depending on how code (in)efficient your microcontroller is.
If you just want to print the sum of int a and int b, you should be able to get rid of the
/------------------------------------------------
Setup the serial port for 1200 baud at 16MHz.
------------------------------------------------/
#ifndef MONITOR51
SCON = 0x50; /* SCON: mode 1, 8-bit UART, enable rcvr /
TMOD |= 0x20; / TMOD: timer 1, mode 2, 8-bit reload /
TH1 = 221; / TH1: reload value for 1200 baud # 16MHz /
TR1 = 1; / TR1: timer 1 run /
TI = 1; / TI: set TI to send first char of UART */
#endif:
`
code. Just keep the printf()... and scarf()... functions.
I have a device, and it has digital i/o, analog i/o. I send to device below commands for communication.The device has gpio module. My device documantation is here
Write to digital input : gpio set/clear x
Read from digital output : gpio read x
Read from digital output : adc read x
(x : pin number)
How can I create sine/square wave and calculate amplitude? To create square wave :
open device
sleep
write to device low mode(t0)
sleep
write to device high mode
sleep
write to device low mode(t1)
period = (t1 - t0)
Is this a square wave?
it seems your example is indeed square wave
if write to device low mode(t0) sets the output pin to low and write to device low mode(t1) to high or reverse then period is the sum of the sleeps + some time for setting up GPIO state. Don't know why you have times inside the GPIO set lines and not in sleeps ... (may be something platform dependent?)
To go to sin wave
use DAC or PWM + RC filter with some precomputed amplitude table in which the index is increasing periodically.
BYTE sintab[32]={ 128,...,255,...,128,...,0,....,127 };
encoded: 128 is zero, 255 is +1 and 0 is -1; now just add some index:
int ix=0'
and once in a while (on some timer perhaps) increment it and set the output to new value:
ix=(ix+1)&31;
that and 31 just cause tho cycle the index from start again if end reached (sintab must be of size power of 2). The period is timer frequency/sintab size
[notes]
You can modify this to your purpose for example make sintab[][] a 2D array where first index means amplitude and second is the ix as now. On older platforms (MCU's) you can encode the PWM sequence directly to sintab end so on ...
You can pre-compute the sintab values like this:
sintab[ix]=128.0+127.0*sin(float(2.0*M_PI*ix)/32.0);
or if your platform supports fast enough sin you can use above line directly without the actual array ...
[edit1]
for sinwave you can use just 0/1 states. If you need analog output and:
You have DAC (digital to analog converter)
then send the actual amplitude to it like dac write sintab[ix]; that will create the analog voltage on the output pin for you.
You do not have any spare DAC's use PWM Pulse Width Modulation instead
it is an old school trick to avoid the need of DAC and still have analog output from digital pin. It works like this:
The output value is the cumulative energy/voltage per time chunk so you generate square-wave signal
ratio 1:1 means that half the period is H and the rest L
ratio 2:1 means that 2/3 of the period is H and the rest L
The more time the output is H the bigger output value. This is still digital output but if you connect on it any nonlinear device like capacitor or coil then the energy inertia will cause to drop the H voltage to some level dependent on the square-wave ratio. Most common is the RC filter (R is serial and C is parallel to ground). If you want to drive some coil (motor) then you do not need the filter. This kind of use usually generate high pitch sound (the PWM frequency) often heard near machinery ...
The PWM frequency has to be high enough (many times higher then the sinwave frequency)
Some code for PWM with amplitude and frequency setting:
const int timer_T=1; // used timer interval [ms]
const int PWM_max=10; // PWM max amplitude+1
int PWM_s=0; // PWM actual step
int PWM_t=0; // PWM actual time
int PWM_a=3; // PWM amplitude <0,PWM_ratio_max)
int PWM_T=200; // PWM period [ms]
void OnTimer()
{
int PWM_T0=PWM_T/PWM_max; // PWM step period must be >=1 !!!
PWM_t+=timer_T;
if (PWM_t>=PWM_T0)
{
if (PWM_s<=pwm_a) gpio set x; else gpio clear x;
PWM_s++; if (PWM_s>=PWM_max) PWM_s=0;
PWM_t-=PWM_T0;
}
}
I am trying to read analogic signal for a sort of mouse with a pic18f14k50 controller. Here the simple circuit: http://dl.dropbox.com/u/14663091/schematiconew.pdf . I have to read analogic signal from AN9 circuit port. Main function reads from the port, and blinks 30 time if threshold is reached:
void main(void) {
InitializeSystem();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1) {
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) continue;
if(!HIDTxHandleBusy(lastTransmission))
{
int readed = myReadADC2(); //Here i tried both myReadADC2() or myReadADC1()
if(readed>40) { //If read threshold > 40, blink led 30 times
int i;
for(i=0; i<30; i++) {
Delay1KTCYx(0);
mLED_1_On();
Delay1KTCYx(0);
mLED_1_Off();
}
}
lastTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
}//end while
}//end main
I used two method to read from the AN9 port, myReadADC() that uses OpenADC() API method:
int myReadADC(void) {
#define ADC_REF_VDD_VDD_X 0b11110011
OpenADC(ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH9 & ADC_INT_OFF, ADC_REF_VDD_VDD_X & ADC_REF_VDD_VSS, 0b00000010); // channel 9
SetChanADC(ADC_CH9);
ConvertADC(); // Start conversion
while(BusyADC()); // Wait for completion
return ReadADC(); // Read result
}
and myReadADC2(), that implements manual read from the port.
int myReadADC2() {
int iRet;
OSCCON=0x70; // Select 16 MHz internal clock
ANSEL = 0b00000010; // Set PORT AN9 to analog input
ANSELH = 0; // Set other PORTS as Digital I/O
/* Init ADC */
ADCON0=0b00100101; // ADC port channel 9 (AN9), Enable ADC
ADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)
ADCON2=0b10101011; // Right justify result, 12 TAD, Select the FRC for 16 MHz
iRet=100;
ADCON0bits.GO=1;
while (ADCON0bits.GO); // Wait conversion done
iRet=ADRESL; // Get the 8 bit LSB result
iRet += (ADRESH << 8); // Get the 2 bit MSB result
return iDelay;
}
Both cases doesn't works, i touch (sending analogic signal) port AN9 but when I set high threshold (~50) led don't blinks, with low threshold (~0) it blinks immidiatly when i provide power to the PIC. Maybe i'm using wrong port? I'm actually passing AN9 as reading port? Or maybe threshold is wrong? How can i found the right value? Thank you
Here the MPLAB C18 Apis http://dl.dropbox.com/u/14663091/API%20microchip%20C18.pdf .
Regarding function myReadADC2(): you need to switch ANSEL and ANSELH configs as RC7/AN9 is configured in bit 1 of ANSELH. Also call me paranoid but for the line
iRet += (ADRESH << 8);
I always like to either save it a temporary variable first or cast explicitly the value ADRESH before shifting it up:
iRet += (((UINT) ADRESH) << 8);
That way I know for sure the bits won't get lost when shifting up which has bitten me before.
Regarding function myReadADC():
OpenADC() only takes two parameters. I presume that bitfield in the third parameter field is for the analog enable (ADRESH/ADRES). I'm assuming that's handled by SetChanADC() but you may have to set ADRESH/ADRES manually. It may help to set a breakpoint in the debugger and stop after configuration is complete to make sure your registers are set appropriatley.