В настоящее время я пытаюсь выяснить, как именно я мог бы просто включить или выключить один из битовых выводов мой модуль который использует чип FT232RL.
В настоящее время я использую следующий исходный код из этого урока (немного изменен для работы в Visual C ++):
/* 8-bit PWM on 4 LEDs using FTDI cable or breakout.
This example uses the D2XX API.
Minimal error checking; written for brevity, not durability. */
#include "stdafx.h"#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ftd2xx.h>
#define LED1 0x08 /* CTS (brown wire on FTDI cable) */
#define LED2 0x01 /* TX (orange) */
#define LED3 0x02 /* RX (yellow) */
#define LED4 0x14 /* RTS (green on FTDI) + DTR (on SparkFun breakout) */
int _tmain(int argc, _TCHAR* argv[])
{
int i,n;
unsigned char data[255 * 256];
FT_HANDLE handle;
DWORD bytes;
/* Generate data for a single PWM 'throb' cycle */
memset(data, 0, sizeof(data));
for(i=1; i<128; i++) {
/* Apply gamma correction to PWM brightness */
n = (int)(pow((double)i / 127.0, 2.5) * 255.0);
memset(&data[i * 255], LED1, n); /* Ramp up */
memset(&data[(256 - i) * 255], LED1, n); /* Ramp down */
}
/* Copy data from first LED to others, offset as appropriate */
n = sizeof(data) / 4;
for(i=0; i<sizeof(data); i++)
{
if(data[i] & LED1) {
data[(i + n ) % sizeof(data)] |= LED2;
data[(i + n * 2) % sizeof(data)] |= LED3;
data[(i + n * 3) % sizeof(data)] |= LED4;
}
}
/* Initialize, open device, set bitbang mode w/5 outputs */
if(FT_Open(0, &handle) != FT_OK) {
puts("Can't open device");
return 1;
}
FT_SetBitMode(handle, LED1 | LED2 | LED3 | LED4, 1);
FT_SetBaudRate(handle, 9600); /* Actually 9600 * 16 */
/* Endless loop: dump precomputed PWM data to the device */
for(;;) FT_Write(handle, &data, (DWORD)sizeof(data), &bytes);_getch();
return 0;
}
Но я просто не понимаю этого. Где в этом коде происходит магия? Есть ли способ поместить значимые части кода в две основные функции Turn_Pin_1_On () и Turn_Pin_1_Off ().
Подробное объяснение встроено в исходный код ниже (ВНИМАНИЕ: это только для мозгового компиляции, поэтому CAVEAT EMPTOR). Однако дух объяснения верен. Временами код может быть немного раздутым, но это сделано для того, чтобы подробно объяснить работу PWM-части, а также основы битовой маскировки.
/* 8-bit PWM on 4 LEDs using FTDI cable or breakout.
This example uses the D2XX API.
Minimal error checking; written for brevity, not durability. */
#include "stdafx.h"#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ftd2xx.h>
#define LED1 0x08 /* CTS (brown wire on FTDI cable) */
#define LED2 0x01 /* TX (orange) */
#define LED3 0x02 /* RX (yellow) */
#define LED4 0x14 /* RTS (green on FTDI) + DTR (on SparkFun breakout) */
/*
WARNING: don't have a FTDI device for proper
testing, so everything here is BRAIN COMPILED.
CAVEAT EMPTOR.
It is very simple really. Every time you send a byte out
either using FT_Write (for large buffers of precomputed bytes),
or using ftdi_write_data(...), the content of your byte directly maps
to the state of those pins. Say I display the content of a byte as follows
with 1 marking a bit that is set, and empty marking a bit that is clear (0)
THESE ARE THE BITS IN YOUR BYTE
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | | | | | |
+------+------+------+------+------+------+------+------+
If FTDI module receives a byte, where:
- bit 0 is set: then orange (TX) wire LED will turn ON
- bit 0 is clear: then orange (TX) wire LED will turn OFF
- bit 1 is set: then yellow (RX) wire LED will turn ON
- bit 1 is clear: then yellow (RX) wire LED will turn OFF
- bit 2 is set: then green (RTS) wire will turn ON
- bit 2 is clear: then green( RTS) wire will turn OFF
- bit 3 is set: then brown (CTS) wire LED will turn ON
- bit 3 is clear: then brown (CTS) wire LED will turn OFF
- bit 4 is set: then DTR(on sparkfun) wire will turn ON
- bit 4 is clear: then DTR(on sparkfun) wire will turn OFF
Now, your defines above for LED1, LED2, LED3, LED4 define the
bit states needed to turn on individual LEDS.
For example, look at your LED1 constant:
LED1 constant (0x08 -> 0b00001000)
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | | X | | | |
+------+------+------+------+------+------+------+------+
...thus sending this directly will turn brown LED ON
AND turn off all other LEDs.
The LED4 actually turns 2 wires on at a time. Because the FTDI only
implements RTS, and the Sparkfun only implements DTR, in practice
one of those wires turn out to be unused by either module, but
for the sake of simplicity the coders decided to control both
bits at the same time
LED4 constant (0x14 -> 0b00010100)
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | X | | X | | |
+------+------+------+------+------+------+------+------+
thus sending this directly will turn green & DTR LEDs ON
AND turn off all other LEDs.
Now, what if, say I want to turn LED 1 and LED4 ON? Then I must
combine both constants together
Example: turn LED1 & LED 4 ON, turn off LED2 & LED 3
Command to be sent: 0x08 + 0x14 = 0x1C
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | X | X | X | | |
+------+------+------+------+------+------+------+------+a simple function that gets the proper formatted byte for a desired LED
state would be:
*/
unsigned char GetByteForLEDState(BOOL isLED1On, BOOL isLED2On, BOOL isLED3On, BOOL isLED4On){
unsigned char ret=0x00;
if(isLED1On) ret += LED1;
if(isLED2On) ret += LED2;
if(isLED3On) ret += LED3;
if(isLED4On) ret += LED4;
return ret;
}
/*
Now, you see that a problem with this method is that you cannot set/clear a
LED off without changing the others at the same time: they all need to be
set/cleared simultaneously. So how do you make it so that you can leave
certain LEDs unchanged while setting/clearing some others, in an easy way?
C offers the bitmasking strategy in order to set/clear bits individually in
a byte without touching others.
Example: I have BYTE STATE = 0x1C,
(LED 1 & LED4 ON), and I need to turn off LED1 but leave LED4 untouched
How do I make that happen? By bitwise ANDing the STATE byte with
the LED1 constant inverted
OLD STATE (bits) original state:
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | X | X | X | | |
+------+------+------+------+------+------+------+------+
BITWISE AND WITH...
Inverse of LED1 constant
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| X | X | X | X | | X | X | X |
+------+------+------+------+------+------+------+------+
YIELDS...
NEW STATE
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | X | | X | | |
+------+------+------+------+------+------+------+------+
STATE = STATE & (0x08)
As you can see, LED1 was turned off, and LED4 is still On.
in practice, the C style operation I just did was this:
STATE = STATE & (~LED1);
To turn an individual LED ON, say LED1 back on,
without touching the others in a given LED state byte,
I do it via bitwise ORing the STATE byte with constant LED1
OLD STATE (bits) original state:
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | X | | X | | |
+------+------+------+------+------+------+------+------+
BITWISE OR WITH...
LED1 constant (0x08 -> 0b00001000)
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | | X | | | |
+------+------+------+------+------+------+------+------+
YIELDS...
NEW STATE
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+------+------+------+------+------+------+------+------+
| | | | X | X | X | | |
+------+------+------+------+------+------+------+------+
In practice, the C style operation I just did was this:
STATE = STATE | LED1;
so what would be the stateless functions that implement those? well:
say we use the specific format for our stateless functions:
*/
unsigned char STATELESS_MODIFIER_FUNCTION(unsigned char originalState){
unsigned char newState = originalState;
/* Do some operations here... */
return newState;
}
/* The specific implementations for PIN1 would go like this: */
unsigned char Turn_Pin_1_On(unsigned char originalState){
return originalState | LED1; /* we use bitwise OR */
}
unsigned char Turn_Pin_1_Off(unsigned char originalState){
return originalState & (~LED1); /*we use bitwise AND with INVERSE */
}
unsigned char Toggle_Pin_1(unsigned char originalState){
return originalState ^ LED1; /*we use bitwise XOR */
}
unsigned char Write_Pin_1_State(unsigned char originalState, BOOL pinState){
if(pinState) return Turn_Pin_1_On();
else return Turn_Pin_1_Off();
}
/*Same for PIN 2: */
unsigned char Turn_Pin_2_On(unsigned char originalState){ return originalState | LED2; } /*we use bitwise OR */
unsigned char Turn_Pin_2_Off(unsigned char originalState){ return originalState & (~LED2); } /*we use bitwise AND with INVERSE */
unsigned char Toggle_Pin_2(unsigned char originalState){ return originalState ^ LED2; } /*we use bitwise XOR */
unsigned char Write_Pin_2_State(unsigned char originalState, BOOL pinState){
if(pinState) return Turn_Pin_2_On();
else return Turn_Pin_2_Off();
}
/*Same for PIN 3: */
unsigned char Turn_Pin_3_On(unsigned char originalState){ return originalState | LED3; } /*we use bitwise OR */
unsigned char Turn_Pin_3_Off(unsigned char originalState){ return originalState & (~LED3); } /*we use bitwise AND with INVERSE */
unsigned char Toggle_Pin_3(unsigned char originalState){ return originalState ^ LED3; } /*we use bitwise XOR */
unsigned char Write_Pin_3_State(unsigned char originalState, BOOL pinState){
if(pinState) return Turn_Pin_3_On();
else return Turn_Pin_3_Off();
}
/*Same for PIN 4: */
unsigned char Turn_Pin_4_On(unsigned char originalState){ return originalState | LED4; } /*we use bitwise OR */
unsigned char Turn_Pin_4_Off(unsigned char originalState){ return originalState & (~LED4); } /*we use bitwise AND with INVERSE */
unsigned char Toggle_Pin_4(unsigned char originalState){ return originalState ^ LED4; } /*we use bitwise XOR */
unsigned char Write_Pin_4_State(unsigned char originalState, BOOL pinState){
if(pinState) return Turn_Pin_4_On();
else return Turn_Pin_4_Off();
}
/*
... same for the other pins, of course.
now, you can also include equivalent stateful functions that
store the last known state of the serial port chip in some
global variable, for simplicity purposes.
*/
unsigned char lastKnownState; /* our global state variable here */
unsigned char GetState(){return lastKnownState;} /* some basic accessor for state */
/* PIN : */
void ChangeState_TurnPin1On(){ lastKnownState = Turn_Pin_4_On(lastKnownState); }
void ChangeState_TurnPin1Off(){ lastKnownState = Turn_Pin_4_Off(lastKnownState); }
void ChangeState_TogglePin1(){ lastKnownState = Toggle_Pin_4(lastKnownState); }
void ChangeState_WritePin1(BOOL pinState){ lastKnownState = Write_Pin_4_State(lastKnownState, pinState); }
/* PIN 2: */
void ChangeState_TurnPin2On(){ lastKnownState = Turn_Pin_4_On(lastKnownState); }
void ChangeState_TurnPin2Off(){ lastKnownState = Turn_Pin_4_Off(lastKnownState); }
void ChangeState_TogglePin2(){ lastKnownState = Toggle_Pin_4(lastKnownState); }
void ChangeState_WritePin2(BOOL pinState){ lastKnownState = Write_Pin_4_State(lastKnownState, pinState); }
/* PIN 3: */
void ChangeState_TurnPin3On(){ lastKnownState = Turn_Pin_4_On(lastKnownState); }
void ChangeState_TurnPin3Off(){ lastKnownState = Turn_Pin_4_Off(lastKnownState); }
void ChangeState_TogglePin3(){ lastKnownState = Toggle_Pin_4(lastKnownState); }
void ChangeState_WritePin3(BOOL pinState){ lastKnownState = Write_Pin_4_State(lastKnownState, pinState); }
/* PIN 4: */
void ChangeState_TurnPin4On(){ lastKnownState = Turn_Pin_4_On(lastKnownState); }
void ChangeState_TurnPin4Off(){ lastKnownState = Turn_Pin_4_Off(lastKnownState); }
void ChangeState_TogglePin4(){ lastKnownState = Toggle_Pin_4(lastKnownState); }
void ChangeState_WritePin4(BOOL pinState){ lastKnownState = Write_Pin_4_State(lastKnownState, pinState); }
/*
Now you see that those functions change the state byte global variable at
any time so in order to use this, say to make an immediate change to the
serial port chip pins in practice, you obviously need to write the state
byte out to the FT device like so (returns TRUE if success):
*/
BOOL TurnPin1On(FT_HANDLE handle){
ChangeState_TurnPin1On();
DWORD bytes;
FT_Write(handle, &lastKnownState, 1, &bytes);
/*
note that our buffer here is a single byte variable!
so in this case we take its memory address using & and we pass it into
the FT_Write function. That function will treat it as any char buffer,
and get its value accordingly.
*/
if(bytes==1) return TRUE;
else return FALSE:
}
/*
... and so forth for your other functions. Now the problem with this approach
is that it is SLOW because you bit-bang once per function call, so you don't
even simultaneously write the new state for all your pins before sending the
command out. Thus this is definitely not an optimal approach. In the original
code you provided below, the coders decided to write every desired byte state
in a periodic sequence, store it in an array of bytes of known size, and then
just send this array over and over again. Thus, this is way faster code,
because you leverage the buffering of the FT chip.
I've modified the code below in order to fit the new functions above, and to
make the whole process clearer. Note that the resulting code is less optimal,
speed-wise, of course. But I guess it is more newbie-friendly so that's a
start.
*/
int _tmain(int argc, _TCHAR* argv[])
{
int i,j,itr,n;
unsigned char triangleWaveTemplate1[256]; /* template for pin 1 */
unsigned char triangleWaveTemplate2[256]; /* template for pin 2 */
unsigned char triangleWaveTemplate3[256]; /* template for pin 3 */
unsigned char triangleWaveTemplate4[256]; /* template for pin 4 */
unsigned char data[256 * 255];
FT_HANDLE handle;
DWORD bytes;
/* Generate triangle wave templates. First we clear all buffers to zero */
memset(data, 0, sizeof(data));
memset(triangleWaveTemplate1, 0, sizeof(triangleWaveTemplate1));
memset(triangleWaveTemplate2, 0, sizeof(triangleWaveTemplate2));
memset(triangleWaveTemplate3, 0, sizeof(triangleWaveTemplate3));
memset(triangleWaveTemplate4, 0, sizeof(triangleWaveTemplate4));
/* write to template for pin 1 */
for(i=1; i<128; i++) {
n = (int)(pow((double)i / 127.0, 2.5) * 255.0);
triangleWaveTemplate1[i] = n; /* triangle wave ramp up */
triangleWaveTemplate1[256-i] = n; /* triangle wave ramp down */
}
/* now generate the other templates for the other pins */
n = sizeof(triangleWaveTemplate1) / 4;
for(i=0; i<sizeof(triangleWaveTemplate2); i++){
/* Pin 2 is an offsetted waveform by 3/4 wavelength to the left
(i.e. 1/4 to the right) versus Pin 1 waveform */
triangleWaveTemplate2[i] = triangleWaveTemplate1[(i + 3*n) % sizeof(triangleWaveTemplate1)];
}
for(i=0; i<sizeof(triangleWaveTemplate3); i++){
/* Pin 3 is an offsetted waveform by 2/4 wavelength versus Pin 1
waveform */
triangleWaveTemplate3[i] = triangleWaveTemplate1[(i + 2*n) % sizeof(triangleWaveTemplate1)];
}
for(i=0; i<sizeof(triangleWaveTemplate4); i++){
/* Pin 4 is an offsetted waveform by 1/4 wavelength to the left
(i.e. 3/4 to the right) versus Pin 1 waveform */
triangleWaveTemplate4[i] = triangleWaveTemplate1[(i + 1*n) % sizeof(triangleWaveTemplate1)];
}
/* now, triangleWaveTemplateX buffers contain the desired triangle waves
for 1 oscillation period for all 4 pins. We can now implement the PWM
thing (basically the inner for loop acts like a sawtooth wave and what
we do is compare its iterator (j) to see if it is below or above a
certain threshold defined by the template buffers. If it is below,
we know the corresponding pin must be on. Else it should be off.*/
itr=0;
for(i=0; i<256;i++){
for(j=0;j<255;j++){
if(j<triangleWaveTemplate1[i]) ChangeState_TurnPin1On();
else ChangeState_TurnPin1Off();
if(j<triangleWaveTemplate2[i]) ChangeState_TurnPin2On();
else ChangeState_TurnPin2Off();
if(j<triangleWaveTemplate3[i]) ChangeState_TurnPin3On();
else ChangeState_TurnPin3Off();
if(j<triangleWaveTemplate4[i]) ChangeState_TurnPin4On();
else ChangeState_TurnPin4Off();
data[itr] = GetState(); /* get resulting state byte for all pins, write in the output data buffer */
}
}
/* var j waveform: sawtooth
v | /| /| /| /|--------/
a | / | / | / |-------/-| /
r | / | / |------/--| / | /
| / |-----/---| / | / | /
i<-|----/----| / | / | / | /
| / | / | / | / | /
t | / ^ | / ^ | / ^ | / ^ | /
h | / | | / | | / | | / | | /
r |/ | |/ | |/ | |/ | |/
e +----------------------------------------------------->
s time | | |
h | ^ | ^ | ^ | ^
o | | | | | | | |
l | | | | | | | |
d | | | | | | | |
| | | | | | | |
| | | | | | | |
L | | | | | | | | |
E |
D |----+ +-----+ +------+ +-------+ +--------
| | | | | | | | |
s | | | | | | | | |
t | | | | | | | | |
a | | | | | | | | |
t | +----+ +---+ +--+ +-+
e +----------------------------------------------------->
time
*//* Initialize, open device, set bitbang mode w/5 outputs */
if(FT_Open(0, &handle) != FT_OK) {
puts("Can't open device");
return 1;
}
FT_SetBitMode(handle, LED1 | LED2 | LED3 | LED4, 1);
FT_SetBaudRate(handle, 9600); /* Actually 9600 * 16 */
/* Endless loop: dump precomputed PWM data to the device */
for(;;) FT_Write(handle, data, (DWORD)sizeof(data), &bytes);
/*
The original article gets the ADDRESS of the POINTER to the data buffer
using &data ? I think this was a mistake because "data" refers to the
pointer to the buffer already! I corrected here. See API for FTDI
function here:
http://www.ftdichip.com/Support/Knowledgebase/index.html?ft_write.htm
*/
_getch();
return 0;
}