[SOLVED] Non Blocking getchar()? [SOLVED]


103 posts   Page 4 of 5   1, 2, 3, 4, 5
by DavidS » Thu Mar 16, 2017 4:08 am
jojopi wrote:
LdB wrote:The two functions are in different libraries again the specification doesn't say what locale translation they should return.
POSIX says that each supported locale shall include the portable character set, basically ASCII. http://pubs.opengroup.org/onlinepubs/96 ... hap06.html

DavidS never provided any evidence that getch() was returning anything other than ASCII codes when ASCII keys were pressed. In the end it magically turned out that getch() was returning ASCII codes after all.

Had DavidS persisted with the termios solutions, he would have found that they work perfectly too.

When I was using the getch as was demonstrated earlier in the thread, every return value was returning a non-printable character, I tested the same way then. It turns out that setting it up with initscr() somehow changed the behaviour, something that if had been mentioned earlier, or clearly documented in the man pages would have saved me a lot of time.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Thu Mar 16, 2017 4:23 am
jojopi wrote:POSIX says that each supported locale shall include the portable character set, basically ASCII.

Correct but it doesn't say that has to be the default it just says it should include :-)

Ncurses sets a global default being codepage ISO-8859-1 in absence of being commanded to do something different for legacy reason it is clearly documented
If the locale is not initialized, the library assumes that characters are printable as in ISO-8859-1, to work with certain legacy programs.

So it meets the specification you just need to set the locale if you want something different and they tell you that. There are many Posix compliant embedded systems where I basically get numbers 1-80 without installing a locale which is also pretty much what a raw USB HID gives you being 1-231.

Again this gets back to internally the locale from a keyboard point of view is nothing more than a 256 byte array and two lines of code to cross translate the key index and somehow this became a big mountain of a problem. This whole post is basically about a 256 byte const table and probably 4 opcodes which need to go into the final executable to make everything right. Whether he puts them in manually or uses a library function which will insert them in is about all your choice comes down to.

Anyhow he is solved and happy lets just let it die.
Last edited by LdB on Thu Mar 16, 2017 4:44 am, edited 1 time in total.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by DavidS » Thu Mar 16, 2017 4:44 am
LdB wrote:
jojopi wrote:POSIX says that each supported locale shall include the portable character set, basically ASCII.

Correct but it doesn't say that has to be the default it just says it should include :-)

Ncurses sets a global default being codepage ISO-8859-1 in absence of being commanded to do something different for legacy reason it is clearly documented
If the locale is not initialized, the library assumes that characters are printable as in ISO-8859-1, to work with certain legacy programs.

So it meets the specification you just need to set the locale if you want something different.

Again this gets back to internally the locale from a keyboard point of view is nothing more than a 256 byte array and two lines of code to cross translate the key index and somehow this became a big mountain of a problem. This whole post is basically about a 256 byte const table and probably 4 opcodes which need to go into the final executable to make everything right. Whether he puts them in manually or uses a library function which will insert them in is about all your choice comes down to.

Thank you for the attempted insult. How is a character value returned (in integer form) of 0x70F1 different from a character value of 0x70F1?

Those are the values that getch() was returning when done as shown earlier in this thread for the keys '+' and '/' respectively. So beings as they are identical values how do I differentiate them?

I mentioned this many times through the questions I asked, though was ignored. Now it works with the addition of initscr(), which had gone completely unmentioned until this morning.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Thu Mar 16, 2017 5:08 am
DavidS wrote:Thank you for the attempted insult. How is a character value returned (in integer form) of 0x70F1 different from a character value of 0x70F1?

Sorry I wasn't trying to insult you in any way just clearly you had got frustrated and were venting it and everyone noticed.

About the codes sorry I missed that I came late into the discussion. If you note in the posts above I said to you, can we have some codes to try and see what is going on as it looks like a codepage issue. If you look at the table you will see why that is.

DavidS wrote: Now it works with the addition of initscr(), which had gone completely unmentioned until this morning.

Again that is not some standard and people will not be able to tell you. You are talking about a vast range of different versions on Linux, running on widely different platforms and different forks of the same libraries and you are trying to say there should be a standard. If you look at the manual it is bit AC/DC on it and to honest I have never used it because I am pretty much always on embedded systems. I have a lot more work to do than that to get my screens ups, if you have played with baremetal on the PI screen you will sort of get the usual things I have to do.

I am not sure how big the recreational community is that write console apps for linux but given you are having trouble finding information probably not large. I am more than happy to help where I can but some of the stuff you are banging on about makes little difference to me in the commercial world and I have little interest in the complaint.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by DavidS » Thu Mar 16, 2017 5:22 am
LdB wrote:
DavidS wrote:Thank you for the attempted insult. How is a character value returned (in integer form) of 0x70F1 different from a character value of 0x70F1?

Sorry I wasn't trying to insult you in any way just clearly you had got frustrated and were venting it and everyone noticed.

Again sorry I missed that I came late into the discussion. If you note in the posts above I said to you, can we have some codes to try and see what is going on and what codepage it has selected. If you look at the table you will see why that is.

DavidS wrote: Now it works with the addition of initscr(), which had gone completely unmentioned until this morning.

Again that is not some standard and people will not be able to tell you. You are talking about a vast range of different versions on Linux, running on widely different platforms and different forks of the same libraries and you are trying to say there should be a standard. If you look at the manual it is bit AC/DC on it and to honest I have never used it because I am pretty much always on embedded systems. I have a lot more work to do than that to get my screens ups, if you have played with baremetal on the PI screen you will sort of get the usual things I have to do.

Fair enough. This program is one that would have taken about 5 lines of code in C on RISC OS, about 15 to 20 ARM assembly instructions in RISC OS, or about 4 to 6 lines of code in BBC BASIC V on RISC OS.

I went through all this as I am really trying to give Linux on the RPi a fair chance, and am continuing to try. I have not done much on any *nix in a very long time, so a lot of this is relearning, or learning anew in some cases for me. My more recent experiences with programming are in RISC OS, FreeDOS, Amiga OS, and Atari TOS (without MiNT) and the Parallax Propeller. This is where I am coming from, and it was feeling as if people were expecting me to have some information that I did not, this was frustrating me quite a bit for a while on this project.

I do admit I came close to swapping SD-Cards on this RPi 3B and having all my RPi's running RISC OS. Though I had promised that I would give Linux a fair chance, and a man is nothing without his word.

As another alternative I almost rewrote it to use xlib instead, as I have had less trouble with X and non-blocking input is the normal state in that case. Though I am thankful for what I learned in this process.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by DavidS » Thu Mar 16, 2017 5:28 am
LdB wrote:All that complaining for the ugliest stepper controller ever ... sorry having programmed for CNC controller manufacturers for 20 years it was funny seeing what you end up using the code for :-)

As an aside if you ever want code/designs for microstep controllers or motion profiles I have quite a library of such stuff that is no longer commercial. Intense competition forced the cost of micro controller units down to under $20 and it's no longer viable to manufacture them for anywhere outside of China.



And to think in RISC OS I could have done it almost without any thought, and it would have looked a lot better, taking only a few lines of code (up to around 20 if done in ARM Assembly). I do still have to look up the GPIO module SWI's and assignments in the !StrongHelp manual when doing this kind of thing, though it is still very easy in RISC OS for me.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Thu Mar 16, 2017 5:41 am
Can I ask what you are actually doing that is a weird way to ramp a stepper usually you have a ramp table?

With what I have seen so far you would be better off just dropping down an assembler block and extern link it to C and you could even sit your control up on the GUI or console makes little difference. On a PI2/3 you put the step code on one of the other CPU cores so none of the UI stuff gets to interfere with timings on the step code.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by DavidS » Thu Mar 16, 2017 5:53 am
LdB wrote:Can I ask what you are actually doing that is a weird way to ramp a stepper usually you have a ramp table?

With what I have seen so far you would be better off just dropping down an assembler block and extern link it to C and you could even sit your control up on the GUI or console makes little difference. On a PI2/3 you put the step code on one of the other CPU cores so none of the UI stuff gets to interfere with timings on the step code.

Yes it is a strange method of ramping, though it does the job in a simple way for the application.

It is being used to wind PLA or ABS fillament that is being made by an extruder, under manual control of speed. Basically somewhere between an auto-winder and completely manual winding. I call it a SimiAuto filament winder, see:
http://www.thingiverse.com/thing:2172754

If you have suggestions for improvement while keeping the code size down I am interested to learn.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Thu Mar 16, 2017 6:09 am
Ok this is the way it's done on almost all CNC machines it's called an EulerLoop

Code: Select all
eulerMax = Loopfrequency;
dx = x step rate
dy = y step rate
tx = x test value
ty = y test value

tx = 0;          // test values start at zero
ty = 0;          // test value starts at zero
while (1){
   tx += dx;
   if (tx >= eulerMax){
      tx -= eulerMax;
      // step the x motor
   }
   ty += dy;
   if (ty >= eulerMax){
      ty -= eulerMax;
      // step the y motor
   }
}


You want that loop going as fast as possible I mean as fast as you can get because it is basically controls granularity. Usually it's running on a dedicated core you don't want any interruption to it at all.

Lets detail how it works so lets assume the loop around frequency is 1Mhz and so we would have eulerMax as 1Mhz. So generally you write the loop and (time it post the fact) and set that loop frequency as eulerMax.

So if I want to step at 1000 pulse per second on the X motor I set dx to 1000.

dx gets added to tx 1000 times and at that point tx exceeds eulerMax and it subtracts and steps. And off it goes again.

If you don't want the motor to move at all you set 0 to dx.

The changing of the values of dx is done on a totally seperate thing (in your case the keyboard) running on a different processor. It generally changes the values of dx (dy) and sets a direction flag so when stepping you know which way to move. That system creates the least jitter possible without going to a physical specialized design hardware which we do for higher end machines.

For me on the Pi I would put that block on one of the spare cores and just change the value from core 0.
Last edited by LdB on Thu Mar 16, 2017 6:22 am, edited 1 time in total.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by DavidS » Thu Mar 16, 2017 6:21 am
LdB wrote:Ok this is the way it's done on almost all CNC machines it's called an EulerLoop

Code: Select all
eulerMax = Loopfrequency;
dx = x step rate
dy = y step rate
tx = x test value
ty = y test value

tx = 0;          // test values start at zero
ty = 0;          // test value starts at zero
while (1){
   tx += dx;
   if (tx >= eulerMax){
      tx -= eulerMax;
      // step the x motor
   }
   ty += dy;
   if (ty >= eulerMax){
      ty -= eulerMax;
      // step the y motor
   }
}


You want that loop going as fast as possible I mean as fast as you can get because it is basically controls granularity. Usually it's running on a dedicated core you don't want any interruption to it at all.

Lets detail how it works so lets assume the loop around frequency is 1Mhz and so we would have eulerMax as 1Mhz. So generally you write the loop and (time it post the fact) and set that loop frequency as eulerMax.

So if I want to step at 1000 pulse per second on the X motor I set dx to 1000.

dx gets added to tx 1000 times and at that point tx exceeds eulerMax and it subtracts and steps. And off it goes again.

If you don't want the motor to move at all you set 0 to dx.

The changing of the values of dx is done on a totally seperate thing (in your case the keyboard) running on a different processor. It generally changes the values of dx (dy) and sets a direction flag so when stepping you know which way to move. That system creates the least jitter possible without going to a physical specialized design hardware which we do for higher end machines.

Now that is somewhat similiar to how I do it on the Parallax Propeller for multi axis applications, though with a loop speed of 5 MHz (3 instructions at 4 cycles per for two and 8 cycles for the other, 20MIPS core), and only one stepper per processing core (there are 8 in the Propeller). Well if I want precision anyway, there are slow cases that I do not care so much about jitter.

Strike the above timing, wrong reference, sorry.

That is a loop time of 2MHz, as it is 80MHz, and all used instructions are 4 cycle (20MIPS), with 9 instructions in the loop (the branch being 8 cycles).
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Thu Mar 16, 2017 6:26 am
I want that loop itself typically above 20Mhz :-)

In some of our dedicated FGPA vhdl setups its many factors above that.

Anyhow if you know that trick then I can add nothing that is by far the best way to do it.

That block is also very simple to write in VHDL code and put into an FPGA which you can easily connect to a microprocessor. So that is how you have really fast CNC's off a moderate CPU core running the more mundane stuff :-)
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by DavidS » Thu Mar 16, 2017 6:46 am
LdB wrote:I want that loop itself typically above 20Mhz :-)

In some of our dedicated FGPA vhdl setups its many factors above that.

Anyhow if you know that trick then I can add nothing that is by far the best way to do it.

That block is also very simple to write in VHDL code and put into an FPGA which you can easily connect to a microprocessor. So that is how you have really fast CNC's off a moderate CPU core running the more mundane stuff :-)

Just thinking about the line capacitance I am having a difficult time picturing any stepper that can change states at a rate above about 4MHz even when counting a micro step as a state change (assuming microstepping of up to 64 micro steps per full step). Are you pushing more than 64 micro steps per full step? You have my interest.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Thu Mar 16, 2017 6:55 am
You don't use stepper motors at those high speed they are digital servos everything works off the standard 4 line step / direction / enable / power down. For most machines they will have a low cost stepper option then it goes to digital servos and then digital servo thru viscous brake. At really high speeds braking the motor becomes problematic if you have to pull up instantly on a collision or emergency stop.

For microstepper units for high speed you will run 128 steps per step. Here this is the VHDL code for a microstepper controller as I said none of this stuff is commercial anymore. The Cosine sine tables are usually output to a resistor ladder or if your upmarket a proper DAC. That does all the cross conduction for phase chopping as well. If it interests you I can give you a number of typical schematics for cheap FPGA's. The reason for X,Y,Z markings is we have a standard bus and you can choose which axis lines it picks up, you set a jumper and it takes those signals from the bus.

If you are interested I can drag out later code where we added power boost which basically monitors the step rate per second and slightly increases power at high speed and a few fancy anti-resonant frequency damping. The advantage of VHDL it's easy to do that stuff so long as you have unused space in the FPGA. That however is the most basic form of a microstepper.

The chopper frequency is 32Khz to be outside audible and on later units we allowed that to be moved a bit as well especially on the higher resolution settings.

I wish I didn't still have boxes of these things taking up room in my shed but we lost the market war :-)
EDIT: Might as well give you circuits here is the basic circuit setup for a low end unit.
https://drive.google.com/open?id=0B-BiE ... EU0dHZNSlk
Code: Select all
LIBRARY IEEE;   USE ieee.std_logic_1164.all;
                USE ieee.std_logic_arith.all;
                USE ieee.std_logic_unsigned.all;


ENTITY cimsrev4 IS
   GENERIC (deadtime: INTEGER := 50;
            maxcount: INTEGER := 624);
   PORT (
      --[ RESET SIGNAL INPUT ACTIVE LOW ]--
      reset: IN STD_LOGIC;

      --[ XTAL OSCILLATOR INPUTS ]--
      clk: IN STD_LOGIC;
      x1: OUT STD_LOGIC;
      osc_out: OUT STD_LOGIC;

      --[ PWM TEST PINS ]--
      pwm_clk_a: BUFFER STD_LOGIC;
      pwm_clk_b: BUFFER STD_LOGIC;
     
      --[ RESOLUTION SWITCH INPUTS ]--
      sw: IN STD_LOGIC_VECTOR (4 downto 1);

      --[ SIGNAL SELECTION INPUTS ]--
      sel_x_inputs: IN STD_LOGIC;
      sel_y_inputs: IN STD_LOGIC;
      sel_z_inputs: IN STD_LOGIC;

      --[ STEP SIGNAL INPUTS ]--
      x_step: IN STD_LOGIC;
      y_step: IN STD_LOGIC;
      z_step: IN STD_LOGIC;
      opt_step: IN STD_LOGIC;

      --[ DIRECTION SIGNAL INPUTS ]--
      x_dir: IN STD_LOGIC;
      y_dir: IN STD_LOGIC;
      z_dir: IN STD_LOGIC;
      opt_dir: IN STD_LOGIC;

      --[ ENABLE SIGNAL INPUTS ]--
      x_enable: IN STD_LOGIC;
      y_enable: IN STD_LOGIC;
      z_enable: IN STD_LOGIC;
      opt_enable: IN STD_LOGIC;

      --[ FET OUTPUTS ]--
      drv2a: OUT STD_LOGIC;
      drv1a: OUT STD_LOGIC;
      ndea: OUT STD_LOGIC;
      drv2b: OUT STD_LOGIC;
      drv1b: OUT STD_LOGIC;
      ndeb: OUT STD_LOGIC;

      --[ LED OUTPUTS ]--
      enable_led: OUT STD_LOGIC;
      step_led: BUFFER STD_LOGIC;

      --[ A TO D COMPARATOR INPUTS ]--
      ani1: IN STD_LOGIC;
      ani2: IN STD_LOGIC;

      --[ D TO A OUTPUTS ]--
      sine: OUT STD_LOGIC_VECTOR (7 downto 0);
      cosine: OUT STD_LOGIC_VECTOR (7 downto 0)   
    );
END cimsrev4;

ARCHITECTURE behaviour OF cimsrev4 IS

   --[ INPUT MUX CONTROL SIGNALS ]--
   SIGNAL step: STD_LOGIC;
   SIGNAL dir: STD_LOGIC;
   SIGNAL enable: STD_LOGIC;
   SIGNAL muxsel: STD_LOGIC_VECTOR (1 downto 0);

   --[ PWM CLOCK SIGNALS ]--
   SIGNAL pwm_clk: STD_LOGIC;
   SIGNAL pwmdivcnt: INTEGER RANGE 0 to maxcount;

   --[ PWM SIGNALS ]--
   SIGNAL phase_a_pwm: STD_LOGIC;
   SIGNAL phase_b_pwm: STD_LOGIC;

   --[ SWITCH RESOLUTION CONTROL SIGNALS ]--
   SIGNAL stepsize: INTEGER RANGE 1 TO 512;
   SIGNAL stepmask: STD_LOGIC_VECTOR (8 downto 0);

   --[ SINE/COSINE VALUE CONTROL SIGNALS ]--
   SIGNAL tablepos: INTEGER RANGE 0 TO 127;
   SIGNAL sinetab:  STD_LOGIC_VECTOR (7 downto 0);

   --[ STEP POSITION CONTROL SIGNALS ]--
   SIGNAL position: STD_LOGIC_VECTOR(8 downto 0);
   SIGNAL temppos: STD_LOGIC_VECTOR(8 downto 0); 

   --[ STATE MACHINE SIGNALS ]--
   TYPE state_values IS (st0, st1, st2);
   SIGNAL pres_state: state_values;
   SIGNAL lastposition: STD_LOGIC_VECTOR (8 downto 0);
BEGIN

   --------------------------------------------------------------------------
   -- CREATE 32Mhz XTAL OSCILLATOR PROCESS
   --------------------------------------------------------------------------
   oscilator: PROCESS (clk)
   BEGIN
      x1 <= NOT clk;                                  -- create inverter
      osc_out <= clk;                                 -- create oscilator out
   END PROCESS oscilator;

   --------------------------------------------------------------------------
   -- SELECT INPUT SOURCES PROCESS
   --------------------------------------------------------------------------
   selinputs: PROCESS (sel_x_inputs, sel_y_inputs, sel_z_inputs)
   BEGIN
      IF (sel_x_inputs = '1') THEN                    -- use x signals selected
         muxsel <= "00";                              -- mux selector to "00"
      ELSE
         IF (sel_y_inputs = '1') THEN                 -- use y signals selected
            muxsel <= "01";                           -- mux selector to "01"
         ELSE
            IF (sel_z_inputs = '1') THEN              -- use z signals selected
               muxsel <= "10";                        -- mux selector to "10"
            ELSE                                      -- opto signals to be used
               muxsel <= "11";                        -- mux selector to "11"
            END IF;         
         END IF;
      END IF;
   END PROCESS selinputs;

   --------------------------------------------------------------------------
   -- STEP INPUT MUX PROCESS
   --------------------------------------------------------------------------
   step_mux: PROCESS (muxsel, x_step, y_step, z_step, opt_step)
   BEGIN
      CASE muxsel IS
         WHEN "00" => step <= x_step;                 -- use x step signal   
         WHEN "01" => step <= y_step;                 -- use y step signal
         WHEN "10" => step <= z_step;                 -- use z step signal
         WHEN "11" => step <= opt_step;               -- use opto step signal
         WHEN OTHERS => step <= '1';                  -- good coding practice
      END CASE;
   END PROCESS step_mux;

   --------------------------------------------------------------------------
   -- DIR INPUT MUX PROCESS
   --------------------------------------------------------------------------
   dir_mux: PROCESS (muxsel, x_dir, y_dir, z_dir, opt_dir)
   BEGIN
      CASE muxsel IS
         WHEN "00" => dir <= x_dir;                   -- use x direction signal
         WHEN "01" => dir <= y_dir;                   -- use y direction signal
         WHEN "10" => dir <= z_dir;                   -- use z direction signal
         WHEN "11" => dir <= opt_dir;                 -- use opto direction signal
         WHEN OTHERS => dir <= '1';                   -- good coding practice
      END CASE;
   END PROCESS dir_mux;

   --------------------------------------------------------------------------
   -- ENABLE INPUT MUX PROCESS
   --------------------------------------------------------------------------
   enable_mux: PROCESS (muxsel, x_enable, y_enable, z_enable, opt_enable)
   BEGIN
      CASE muxsel IS
         WHEN "00" => enable <= x_enable;             -- use x enable signal
         WHEN "01" => enable <= y_enable;             -- use y enable signal
         WHEN "10" => enable <= z_enable;             -- use z enable signal
         WHEN "11" => enable <= opt_enable;           -- use opto enable signal
         WHEN OTHERS => enable <= '0';                -- good coding practice
      END CASE;
   END PROCESS enable_mux;

   ---------------------------------------------------------------------------
   -- CREATE PWM CLOCK SIGNALS PROCESS
   ---------------------------------------------------------------------------
   pwmclock: PROCESS (reset, clk)
   BEGIN
      IF (reset = '0') THEN                           -- reset active
         pwmdivcnt <= maxcount;                       -- preset max count
         pwm_clk <= '0';                              -- start clock on low
      ELSIF rising_edge(clk) THEN
         IF (pwmdivcnt = 0) THEN
            pwmdivcnt <= maxcount;                    -- reset max count
            pwm_clk <= NOT pwm_clk;                   -- invert PWM clock
         ELSE
            pwmdivcnt <= pwmdivcnt - 1;               -- decrement count
         END IF;
      END IF;
   END PROCESS pwmclock;

   ---------------------------------------------------------------------------
   -- CREATE PWM GATE SIGNALS PROCESS
   ---------------------------------------------------------------------------
   gate_pwm: PROCESS (reset, pwm_clk, pwmdivcnt)
   BEGIN
      IF (reset = '0') OR (pwmdivcnt < deadtime) THEN
         pwm_clk_a <= '0';                           -- pwm clock a to low
      pwm_clk_b <= '0';                           -- pwm clock b to low
      ELSE
         IF (pwm_clk = '0') THEN                     -- first half of cycle
            pwm_clk_a <= '1';                        -- side a can be on
            pwm_clk_b <= '0';                        -- side b is off
         ELSE                                        -- second half of cycle
            pwm_clk_a <= '0';                        -- side a is off
            pwm_clk_b <= '1';                        -- side b can be on
         END IF;
      END IF;
   END PROCESS gate_pwm;

   --------------------------------------------------------------------------
   -- PHASE A PWM OUTPUT PROCESS
   --------------------------------------------------------------------------
   pwm_a: PROCESS (pwm_clk_a, ani1)
   BEGIN
      IF (pwm_clk_a = '0') THEN
         phase_a_pwm <= '1';
      ELSE
         phase_a_pwm <= ani1;
      END IF;
   END PROCESS pwm_a;
   
   --------------------------------------------------------------------------
   -- PHASE B PWM OUTPUT PROCESS
   --------------------------------------------------------------------------
   pwm_b: PROCESS (pwm_clk_b, ani2)
   BEGIN
      IF (pwm_clk_b = '0') THEN
         phase_b_pwm <= '1';
      ELSE
         phase_b_pwm <= ani2;
      END IF;
   END PROCESS pwm_b;   
   
   --------------------------------------------------------------------------
   -- DRIVE ENABLE OUTPUT SIGNALS PROCESS
   --------------------------------------------------------------------------
   drive_enable: PROCESS (reset, enable)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active                 
         ndea <= '0';                                 -- ensure drive A off
         ndeb <= '0';                                 -- ensure drive B off
         enable_led <= '0';                           -- enable led off
      ELSE                                            -- not in reset
         ndea <= enable;                              -- follow enable signal
         ndeb <= enable;                              -- follow enable signal 
         enable_led <= enable;                        -- follow enable signal
      END IF;
   END PROCESS drive_enable;
   
   --------------------------------------------------------------------------
   -- PHASE A OUTPUT SIGNALS PROCESS
   --------------------------------------------------------------------------
   phase_a_signals: PROCESS (position, phase_a_pwm)
   BEGIN
      IF (position(8) = '0') THEN                     -- in quad 1 or quad 2
         drv2a <= '1';                                -- hold signal high
         drv1a <= phase_a_pwm;                        -- reflect pwm signal
      ELSE                                            -- in quad 3 or quad 4
         drv2a <= phase_a_pwm;                        -- reflect pwm signal
         drv1a <= '1';                                -- hold signal high
      END IF;
   END PROCESS phase_a_signals;

   --------------------------------------------------------------------------
   -- PHASE B OUTPUT SIGNALS PROCESS
   --------------------------------------------------------------------------
   phase_b_signals: PROCESS (position, phase_b_pwm)
   BEGIN
      IF ((position(8) XOR position(7)) = '0') THEN   -- in quad 1 or quad 4                 
         drv2b <= '1';                                -- hold signal high
         drv1b <= phase_b_pwm;                        -- reflect pwm signal
      ELSE                                            -- in quad 2 or quad 3
         drv2b <= phase_b_pwm;                        -- reflect pwm signal
         drv1b <= '1';                                -- hold signal high
      END IF;
   END PROCESS phase_b_signals;   

   --------------------------------------------------------------------------
   -- SINE TABLE DATA PROCESS
   --------------------------------------------------------------------------
   sine_table: PROCESS (tablepos)
   BEGIN
      CASE tablepos IS
         WHEN 0 => sinetab <= "00000000";     -- 00H
         WHEN 1 => sinetab <= "00000011";     -- 03H
         WHEN 2 => sinetab <= "00000110";     -- 06H
         WHEN 3 => sinetab <= "00001001";     -- 09H
         WHEN 4 => sinetab <= "00001101";     -- 0DH
         WHEN 5 => sinetab <= "00010000";     -- 10H
         WHEN 6 => sinetab <= "00010011";     -- 13H
         WHEN 7 => sinetab <= "00010110";     -- 16H
         WHEN 8 => sinetab <= "00011001";     -- 19H
         WHEN 9 => sinetab <= "00011100";     -- 1CH
         WHEN 10 => sinetab <= "00011111";    -- 1FH
         WHEN 11 => sinetab <= "00100011";    -- 23H
         WHEN 12 => sinetab <= "00100110";    -- 26H
         WHEN 13 => sinetab <= "00101001";    -- 29H
         WHEN 14 => sinetab <= "00101100";    -- 2CH
         WHEN 15 => sinetab <= "00101111";    -- 2FH
         WHEN 16 => sinetab <= "00110010";    -- 32H
         WHEN 17 => sinetab <= "00110101";    -- 35H
         WHEN 18 => sinetab <= "00111000";    -- 38H
         WHEN 19 => sinetab <= "00111011";    -- 3BH
         WHEN 20 => sinetab <= "00111110";    -- 3EH
         WHEN 21 => sinetab <= "01000001";    -- 41H
         WHEN 22 => sinetab <= "01000101";    -- 45H
         WHEN 23 => sinetab <= "01001000";    -- 48H
         WHEN 24 => sinetab <= "01001011";    -- 4BH
         WHEN 25 => sinetab <= "01001110";    -- 4EH
         WHEN 26 => sinetab <= "01010001";    -- 51H
         WHEN 27 => sinetab <= "01010100";    -- 54H
         WHEN 28 => sinetab <= "01010111";    -- 57H
         WHEN 29 => sinetab <= "01011010";    -- 5AH
         WHEN 30 => sinetab <= "01011100";    -- 5CH
         WHEN 31 => sinetab <= "01011111";    -- 5FH
         WHEN 32 => sinetab <= "01100010";    -- 62H
         WHEN 33 => sinetab <= "01100101";    -- 65H
         WHEN 34 => sinetab <= "01101000";    -- 68H
         WHEN 35 => sinetab <= "01101011";    -- 6BH
         WHEN 36 => sinetab <= "01101110";    -- 6EH
         WHEN 37 => sinetab <= "01110001";    -- 71H
         WHEN 38 => sinetab <= "01110011";    -- 73H
         WHEN 39 => sinetab <= "01110110";    -- 76H
         WHEN 40 => sinetab <= "01111001";    -- 79H
         WHEN 41 => sinetab <= "01111100";    -- 7CH
         WHEN 42 => sinetab <= "01111111";    -- 7FH
         WHEN 43 => sinetab <= "10000001";    -- 81H
         WHEN 44 => sinetab <= "10000100";    -- 84H
         WHEN 45 => sinetab <= "10000111";    -- 87H
         WHEN 46 => sinetab <= "10001001";    -- 89H
         WHEN 47 => sinetab <= "10001100";    -- 8CH
         WHEN 48 => sinetab <= "10001111";    -- 8FH
         WHEN 49 => sinetab <= "10010001";    -- 91H
         WHEN 50 => sinetab <= "10010100";    -- 94H
         WHEN 51 => sinetab <= "10010110";    -- 96H
         WHEN 52 => sinetab <= "10011001";    -- 99H
         WHEN 53 => sinetab <= "10011011";    -- 9BH
         WHEN 54 => sinetab <= "10011110";    -- 9EH
         WHEN 55 => sinetab <= "10100000";    -- A0H
         WHEN 56 => sinetab <= "10100011";    -- A3H
         WHEN 57 => sinetab <= "10100101";    -- A5H
         WHEN 58 => sinetab <= "10101000";    -- A8H
         WHEN 59 => sinetab <= "10101010";    -- AAH
         WHEN 60 => sinetab <= "10101100";    -- ACH
         WHEN 61 => sinetab <= "10101111";    -- AFH
         WHEN 62 => sinetab <= "10110001";    -- B1H
         WHEN 63 => sinetab <= "10110011";    -- B3H
         WHEN 64 => sinetab <= "10110101";    -- B5H
         WHEN 65 => sinetab <= "10111000";    -- B8H
         WHEN 66 => sinetab <= "10111010";    -- BAH
         WHEN 67 => sinetab <= "10111100";    -- BCH
         WHEN 68 => sinetab <= "10111110";    -- BEH
         WHEN 69 => sinetab <= "11000000";    -- C0H
         WHEN 70 => sinetab <= "11000010";    -- C2H
         WHEN 71 => sinetab <= "11000100";    -- C4H
         WHEN 72 => sinetab <= "11000110";    -- C6H
         WHEN 73 => sinetab <= "11001000";    -- C8H
         WHEN 74 => sinetab <= "11001010";    -- CAH
         WHEN 75 => sinetab <= "11001100";    -- CCH
         WHEN 76 => sinetab <= "11001110";    -- CEH
         WHEN 77 => sinetab <= "11010000";    -- D0H
         WHEN 78 => sinetab <= "11010010";    -- D2H
         WHEN 79 => sinetab <= "11010011";    -- D3H
         WHEN 80 => sinetab <= "11010101";    -- D5H
         WHEN 81 => sinetab <= "11010111";    -- D7H
         WHEN 82 => sinetab <= "11011001";    -- D9H
         WHEN 83 => sinetab <= "11011010";    -- DAH
         WHEN 84 => sinetab <= "11011100";    -- DCH
         WHEN 85 => sinetab <= "11011101";    -- DDH
         WHEN 86 => sinetab <= "11011111";    -- DFH
         WHEN 87 => sinetab <= "11100000";    -- E0H
         WHEN 88 => sinetab <= "11100010";    -- E2H
         WHEN 89 => sinetab <= "11100011";    -- E3H
         WHEN 90 => sinetab <= "11100101";    -- E5H
         WHEN 91 => sinetab <= "11100110";    -- E6H
         WHEN 92 => sinetab <= "11100111";    -- E7H
         WHEN 93 => sinetab <= "11101001";    -- E9H
         WHEN 94 => sinetab <= "11101010";    -- EAH
         WHEN 95 => sinetab <= "11101011";    -- EBH
         WHEN 96 => sinetab <= "11101100";    -- ECH
         WHEN 97 => sinetab <= "11101110";    -- EEH
         WHEN 98 => sinetab <= "11101111";    -- EFH
         WHEN 99 => sinetab <= "11110000";    -- F0H
         WHEN 100 => sinetab <= "11110001";   -- F1H
         WHEN 101 => sinetab <= "11110010";   -- F2H
         WHEN 102 => sinetab <= "11110011";   -- F3H
         WHEN 103 => sinetab <= "11110100";   -- F4H
         WHEN 104 => sinetab <= "11110101";   -- F5H
         WHEN 105 => sinetab <= "11110110";   -- F6H
         WHEN 106 => sinetab <= "11110110";   -- F6H
         WHEN 107 => sinetab <= "11110111";   -- F7H
         WHEN 108 => sinetab <= "11111000";   -- F8H
         WHEN 109 => sinetab <= "11111001";   -- F9H
         WHEN 110 => sinetab <= "11111001";   -- F9H
         WHEN 111 => sinetab <= "11111010";   -- FAH
         WHEN 112 => sinetab <= "11111011";   -- FBH
         WHEN 113 => sinetab <= "11111011";   -- FBH
         WHEN 114 => sinetab <= "11111100";   -- FCH
         WHEN 115 => sinetab <= "11111100";   -- FCH
         WHEN 116 => sinetab <= "11111101";   -- FDH
         WHEN 117 => sinetab <= "11111101";   -- FDH
         WHEN 118 => sinetab <= "11111101";   -- FDH
         WHEN 119 => sinetab <= "11111110";   -- FEH
         WHEN 120 => sinetab <= "11111110";   -- FEH
         WHEN 121 => sinetab <= "11111110";   -- FEH
         WHEN 122 => sinetab <= "11111111";   -- FFH
         WHEN 123 => sinetab <= "11111111";   -- FFH
         WHEN 124 => sinetab <= "11111111";   -- FFH
         WHEN 125 => sinetab <= "11111111";   -- FFH
         WHEN 126 => sinetab <= "11111111";   -- FFH
         WHEN 127 => sinetab <= "11111111";   -- FFH
      END CASE;
   END PROCESS sine_table;

   --------------------------------------------------------------------------
   -- TABLE SELECTOR PROCESS
   --------------------------------------------------------------------------
   table_sel: PROCESS (pres_state, position, tablepos)
   VARIABLE shortpos: STD_LOGIC_VECTOR (6 downto 0);
   VARIABLE sinepos: STD_LOGIC_VECTOR (6 downto 0);
   VARIABLE cosinepos: STD_LOGIC_VECTOR (6 downto 0);
   BEGIN
      shortpos := position(6 downto 0);               -- use last bits of position      
      IF (position(7) = '0') THEN                     -- quadrant 1 or quadrant 3
         sinepos := shortpos;                         -- use short position
      ELSE
         sinepos := 127 - shortpos;                   -- invert short position
      END IF;
      IF (position(7) = '1') THEN                     -- quadrant 2 or quadrant 4
         cosinepos := shortpos;                       -- use short position
      ELSE
         cosinepos := 127 - shortpos;                 -- invert short position
      END IF;
      CASE pres_state IS
         WHEN st0 => tablepos <= 0;                    -- return zero
         WHEN st1 => tablepos <= conv_integer(sinepos);-- take sine value
         WHEN st2 => tablepos <= conv_integer(cosinepos);-- take cosine value
      END CASE;
   END PROCESS table_sel;

   --------------------------------------------------------------------------
   -- STATE MACHINE PROCESS
   --------------------------------------------------------------------------
   fsm: PROCESS (reset, clk, pres_state, position)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active
         pres_state <= st0;                           -- set initial state
         lastposition <= (OTHERS => '0');             -- zero last position
         sine <= (OTHERS => '0');                     -- zero sine value
         cosine <= (OTHERS => '1');                   -- cosine to max value
      ELSIF rising_edge(clk) THEN                      -- positive clock edge
         CASE pres_state IS
            WHEN st0 =>                               -- STATE 0
               IF (position = lastposition) THEN      -- compare positions
                  pres_state <= st0;                  -- wait for move
               ELSE
                  lastposition <= position;           -- update last position
                  pres_state <= st1;                  -- move to next state                
               END IF;
            WHEN st1 =>                               -- STATE 1
               sine <= sinetab;                       -- latch sine result 
               pres_state <= st2;                     -- move to next state               
            WHEN st2 =>                               -- STATE 2 
               cosine <= sinetab;                     -- latch cosine result
               pres_state <= st0;                     -- move to initial state
         END CASE;
      END IF;
   END PROCESS fsm;

   --------------------------------------------------------------------------
   -- STEP PROCESS
   --------------------------------------------------------------------------
   dostep: PROCESS (reset, step, dir, stepsize, position)
   BEGIN
      IF (reset = '0') THEN                          -- reset is active
         temppos <= (OTHERS => '0');                 -- zero temp position
         step_led <= '0';                            -- step led off
      ELSIF falling_edge(step) THEN                  -- we are to step
         step_led <= NOT step_led;                   -- invert step led
         IF (dir = '1') THEN                         -- check direction
            temppos <= position + stepsize;          -- move forward
         ELSE
            temppos <= position - stepsize;          -- move backward
         END IF;
      END IF;
   END PROCESS dostep;

   --------------------------------------------------------------------------
   -- STEP POSITION PROCESS
   --------------------------------------------------------------------------
   step_position: PROCESS (reset, temppos, stepmask)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active
         position <= (OTHERS => '0');                 -- zero position
      ELSE
         position <= temppos AND stepmask;            -- mask off bits       
   END IF;
   END PROCESS step_position;

   --------------------------------------------------------------------------
   -- RESOLUTION SWITCH SETTINGS PROCESS
   --------------------------------------------------------------------------
   res_select: PROCESS (sw)
   BEGIN
      CASE sw IS
         WHEN "1101" => stepsize <= 1;                -- 250 microsteps per step
         WHEN "1100" => stepsize <= 2;                -- 125 microsteps per step
         WHEN "1011" => stepsize <= 5;                -- 50 microsteps per step
         WHEN "1010" => stepsize <= 10;               -- 25 microsteps per step
         WHEN "1001" => stepsize <= 25;               -- 10 microsteps per step
         WHEN "1000" => stepsize <= 50;               -- 5 microsteps per step
         WHEN "0111" => stepsize <= 1;                -- 256 microsteps per step
         WHEN "0110" => stepsize <= 2;                -- 128 microsteps per step
         WHEN "0101" => stepsize <= 4;                -- 64 microsteps per step
         WHEN "0100" => stepsize <= 8;                -- 32 microsteps per step
         WHEN "0011" => stepsize <= 16;               -- 16 microsteps per step
         WHEN "0010" => stepsize <= 32;               -- 8 microsteps per step
         WHEN "0001" => stepsize <= 64;               -- 4 microsteps per step
         WHEN "0000" => stepsize <= 128;              -- 2 microsteps per step
         WHEN OTHERS => stepsize <= 16;               -- good coding practice
      END CASE;
   END PROCESS res_select;

   --------------------------------------------------------------------------
   -- MASK FOR STEP SIZE PROCESS
   --------------------------------------------------------------------------
   stepsize_mask: PROCESS (reset, stepsize)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active
         stepmask <= (OTHERS => '1');                 -- set all mask bits
      ELSE 
         stepmask <= NOT CONV_STD_LOGIC_VECTOR(stepsize-1, 9);
      END IF;
   END PROCESS stepsize_mask;

END behaviour;
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by jahboater » Thu Mar 16, 2017 9:49 am
Hi David,

Finding out about ncurses.

The ncurses (== curses nowadays) library is always included within Linux, but the header file ncurses.h is not, ditto the man pages.

So "sudo apt install ncurses-dev ncurses-doc" gets them both, and you can then type "man getch" and so on.

Of course you need to know what to look up. Curses has been around so long (since the 80's) that its all over the internet, so google "ncurses examples" should get you many guides, how-to's and hello world examples. Or google "ncurses getch" say if you don't want to install the help (even search for "ncurses non-blocking" !).

Another source of info is the header file itself /usr/include/ncurses.h
If you call "keypad( stdscr, true )" at the start of your program after initscr() then the special keys get mapped onto single integers above 255. Search in the header file for KEY_DOWN for example, and you will see a list of the supported keys that getch() can return. KEY_DOWN is the down arrow key of course. You can create your own mappings with define_key().

If you want some output by the way, try addstr( "message" ), and move( y, x ) to set the cursor position.

Curses is about optimization as well as portably and simply managing text mode screens. It maintains a separate screen buffer that things like addstr() write to. When you eventually do a refresh() or call getch() (which itself first calls refresh()), curses works out the minimum set of changes, using any special features of the terminal, and updates the real screen as fast as possible.
Posts: 1140
Joined: Wed Feb 04, 2015 6:38 pm
by jojopi » Thu Mar 16, 2017 11:06 am
DavidS wrote:When I was using the getch as was demonstrated earlier in the thread, every return value was returning a non-printable character, I tested the same way then. It turns out that setting it up with initscr() somehow changed the behaviour, something that if had been mentioned earlier, or clearly documented in the man pages would have saved me a lot of time.
Yes, if you fail to initialize the curses library, then getch() cannot read the terminal at all. Perhaps some posts could have made that requirement clearer.

But here you quoted code that does call initscr(), and claimed it was printing values other than ERR or ASCII. And here you quoted code that disengages canonical input processing, and claimed it was line buffering.

I can only conclude that you have been shortening some of the examples given, before you even test them. It is nice to have short test programs, but it is never a good idea to remove lines of code unless you know what they are for.
User avatar
Posts: 2929
Joined: Tue Oct 11, 2011 8:38 pm
by DavidS » Thu Mar 16, 2017 2:39 pm
LdB wrote:You don't use stepper motors at those high speed they are digital servos everything works off the standard 4 line step / direction / enable / power down. For most machines they will have a low cost stepper option then it goes to digital servos and then digital servo thru viscous brake. At really high speeds braking the motor becomes problematic if you have to pull up instantly on a collision or emergency stop.

For microstepper units for high speed you will run 128 steps per step. Here this is the VHDL code for a microstepper controller as I said none of this stuff is commercial anymore. The Cosine sine tables are usually output to a resistor ladder or if your upmarket a proper DAC. That does all the cross conduction for phase chopping as well. If it interests you I can give you a number of typical schematics for cheap FPGA's. The reason for X,Y,Z markings is we have a standard bus and you can choose which axis lines it picks up, you set a jumper and it takes those signals from the bus.

If you are interested I can drag out later code where we added power boost which basically monitors the step rate per second and slightly increases power at high speed and a few fancy anti-resonant frequency damping. The advantage of VHDL it's easy to do that stuff so long as you have unused space in the FPGA. That however is the most basic form of a microstepper.

The chopper frequency is 32Khz to be outside audible and on later units we allowed that to be moved a bit as well especially on the higher resolution settings.

I wish I didn't still have boxes of these things taking up room in my shed but we lost the market war :-)
EDIT: Might as well give you circuits here is the basic circuit setup for a low end unit.
https://drive.google.com/open?id=0B-BiE ... EU0dHZNSlk
Code: Select all
LIBRARY IEEE;   USE ieee.std_logic_1164.all;
                USE ieee.std_logic_arith.all;
                USE ieee.std_logic_unsigned.all;


ENTITY cimsrev4 IS
   GENERIC (deadtime: INTEGER := 50;
            maxcount: INTEGER := 624);
   PORT (
      --[ RESET SIGNAL INPUT ACTIVE LOW ]--
      reset: IN STD_LOGIC;

      --[ XTAL OSCILLATOR INPUTS ]--
      clk: IN STD_LOGIC;
      x1: OUT STD_LOGIC;
      osc_out: OUT STD_LOGIC;

      --[ PWM TEST PINS ]--
      pwm_clk_a: BUFFER STD_LOGIC;
      pwm_clk_b: BUFFER STD_LOGIC;
     
      --[ RESOLUTION SWITCH INPUTS ]--
      sw: IN STD_LOGIC_VECTOR (4 downto 1);

      --[ SIGNAL SELECTION INPUTS ]--
      sel_x_inputs: IN STD_LOGIC;
      sel_y_inputs: IN STD_LOGIC;
      sel_z_inputs: IN STD_LOGIC;

      --[ STEP SIGNAL INPUTS ]--
      x_step: IN STD_LOGIC;
      y_step: IN STD_LOGIC;
      z_step: IN STD_LOGIC;
      opt_step: IN STD_LOGIC;

      --[ DIRECTION SIGNAL INPUTS ]--
      x_dir: IN STD_LOGIC;
      y_dir: IN STD_LOGIC;
      z_dir: IN STD_LOGIC;
      opt_dir: IN STD_LOGIC;

      --[ ENABLE SIGNAL INPUTS ]--
      x_enable: IN STD_LOGIC;
      y_enable: IN STD_LOGIC;
      z_enable: IN STD_LOGIC;
      opt_enable: IN STD_LOGIC;

      --[ FET OUTPUTS ]--
      drv2a: OUT STD_LOGIC;
      drv1a: OUT STD_LOGIC;
      ndea: OUT STD_LOGIC;
      drv2b: OUT STD_LOGIC;
      drv1b: OUT STD_LOGIC;
      ndeb: OUT STD_LOGIC;

      --[ LED OUTPUTS ]--
      enable_led: OUT STD_LOGIC;
      step_led: BUFFER STD_LOGIC;

      --[ A TO D COMPARATOR INPUTS ]--
      ani1: IN STD_LOGIC;
      ani2: IN STD_LOGIC;

      --[ D TO A OUTPUTS ]--
      sine: OUT STD_LOGIC_VECTOR (7 downto 0);
      cosine: OUT STD_LOGIC_VECTOR (7 downto 0)   
    );
END cimsrev4;

ARCHITECTURE behaviour OF cimsrev4 IS

   --[ INPUT MUX CONTROL SIGNALS ]--
   SIGNAL step: STD_LOGIC;
   SIGNAL dir: STD_LOGIC;
   SIGNAL enable: STD_LOGIC;
   SIGNAL muxsel: STD_LOGIC_VECTOR (1 downto 0);

   --[ PWM CLOCK SIGNALS ]--
   SIGNAL pwm_clk: STD_LOGIC;
   SIGNAL pwmdivcnt: INTEGER RANGE 0 to maxcount;

   --[ PWM SIGNALS ]--
   SIGNAL phase_a_pwm: STD_LOGIC;
   SIGNAL phase_b_pwm: STD_LOGIC;

   --[ SWITCH RESOLUTION CONTROL SIGNALS ]--
   SIGNAL stepsize: INTEGER RANGE 1 TO 512;
   SIGNAL stepmask: STD_LOGIC_VECTOR (8 downto 0);

   --[ SINE/COSINE VALUE CONTROL SIGNALS ]--
   SIGNAL tablepos: INTEGER RANGE 0 TO 127;
   SIGNAL sinetab:  STD_LOGIC_VECTOR (7 downto 0);

   --[ STEP POSITION CONTROL SIGNALS ]--
   SIGNAL position: STD_LOGIC_VECTOR(8 downto 0);
   SIGNAL temppos: STD_LOGIC_VECTOR(8 downto 0); 

   --[ STATE MACHINE SIGNALS ]--
   TYPE state_values IS (st0, st1, st2);
   SIGNAL pres_state: state_values;
   SIGNAL lastposition: STD_LOGIC_VECTOR (8 downto 0);
BEGIN

   --------------------------------------------------------------------------
   -- CREATE 32Mhz XTAL OSCILLATOR PROCESS
   --------------------------------------------------------------------------
   oscilator: PROCESS (clk)
   BEGIN
      x1 <= NOT clk;                                  -- create inverter
      osc_out <= clk;                                 -- create oscilator out
   END PROCESS oscilator;

   --------------------------------------------------------------------------
   -- SELECT INPUT SOURCES PROCESS
   --------------------------------------------------------------------------
   selinputs: PROCESS (sel_x_inputs, sel_y_inputs, sel_z_inputs)
   BEGIN
      IF (sel_x_inputs = '1') THEN                    -- use x signals selected
         muxsel <= "00";                              -- mux selector to "00"
      ELSE
         IF (sel_y_inputs = '1') THEN                 -- use y signals selected
            muxsel <= "01";                           -- mux selector to "01"
         ELSE
            IF (sel_z_inputs = '1') THEN              -- use z signals selected
               muxsel <= "10";                        -- mux selector to "10"
            ELSE                                      -- opto signals to be used
               muxsel <= "11";                        -- mux selector to "11"
            END IF;         
         END IF;
      END IF;
   END PROCESS selinputs;

   --------------------------------------------------------------------------
   -- STEP INPUT MUX PROCESS
   --------------------------------------------------------------------------
   step_mux: PROCESS (muxsel, x_step, y_step, z_step, opt_step)
   BEGIN
      CASE muxsel IS
         WHEN "00" => step <= x_step;                 -- use x step signal   
         WHEN "01" => step <= y_step;                 -- use y step signal
         WHEN "10" => step <= z_step;                 -- use z step signal
         WHEN "11" => step <= opt_step;               -- use opto step signal
         WHEN OTHERS => step <= '1';                  -- good coding practice
      END CASE;
   END PROCESS step_mux;

   --------------------------------------------------------------------------
   -- DIR INPUT MUX PROCESS
   --------------------------------------------------------------------------
   dir_mux: PROCESS (muxsel, x_dir, y_dir, z_dir, opt_dir)
   BEGIN
      CASE muxsel IS
         WHEN "00" => dir <= x_dir;                   -- use x direction signal
         WHEN "01" => dir <= y_dir;                   -- use y direction signal
         WHEN "10" => dir <= z_dir;                   -- use z direction signal
         WHEN "11" => dir <= opt_dir;                 -- use opto direction signal
         WHEN OTHERS => dir <= '1';                   -- good coding practice
      END CASE;
   END PROCESS dir_mux;

   --------------------------------------------------------------------------
   -- ENABLE INPUT MUX PROCESS
   --------------------------------------------------------------------------
   enable_mux: PROCESS (muxsel, x_enable, y_enable, z_enable, opt_enable)
   BEGIN
      CASE muxsel IS
         WHEN "00" => enable <= x_enable;             -- use x enable signal
         WHEN "01" => enable <= y_enable;             -- use y enable signal
         WHEN "10" => enable <= z_enable;             -- use z enable signal
         WHEN "11" => enable <= opt_enable;           -- use opto enable signal
         WHEN OTHERS => enable <= '0';                -- good coding practice
      END CASE;
   END PROCESS enable_mux;

   ---------------------------------------------------------------------------
   -- CREATE PWM CLOCK SIGNALS PROCESS
   ---------------------------------------------------------------------------
   pwmclock: PROCESS (reset, clk)
   BEGIN
      IF (reset = '0') THEN                           -- reset active
         pwmdivcnt <= maxcount;                       -- preset max count
         pwm_clk <= '0';                              -- start clock on low
      ELSIF rising_edge(clk) THEN
         IF (pwmdivcnt = 0) THEN
            pwmdivcnt <= maxcount;                    -- reset max count
            pwm_clk <= NOT pwm_clk;                   -- invert PWM clock
         ELSE
            pwmdivcnt <= pwmdivcnt - 1;               -- decrement count
         END IF;
      END IF;
   END PROCESS pwmclock;

   ---------------------------------------------------------------------------
   -- CREATE PWM GATE SIGNALS PROCESS
   ---------------------------------------------------------------------------
   gate_pwm: PROCESS (reset, pwm_clk, pwmdivcnt)
   BEGIN
      IF (reset = '0') OR (pwmdivcnt < deadtime) THEN
         pwm_clk_a <= '0';                           -- pwm clock a to low
      pwm_clk_b <= '0';                           -- pwm clock b to low
      ELSE
         IF (pwm_clk = '0') THEN                     -- first half of cycle
            pwm_clk_a <= '1';                        -- side a can be on
            pwm_clk_b <= '0';                        -- side b is off
         ELSE                                        -- second half of cycle
            pwm_clk_a <= '0';                        -- side a is off
            pwm_clk_b <= '1';                        -- side b can be on
         END IF;
      END IF;
   END PROCESS gate_pwm;

   --------------------------------------------------------------------------
   -- PHASE A PWM OUTPUT PROCESS
   --------------------------------------------------------------------------
   pwm_a: PROCESS (pwm_clk_a, ani1)
   BEGIN
      IF (pwm_clk_a = '0') THEN
         phase_a_pwm <= '1';
      ELSE
         phase_a_pwm <= ani1;
      END IF;
   END PROCESS pwm_a;
   
   --------------------------------------------------------------------------
   -- PHASE B PWM OUTPUT PROCESS
   --------------------------------------------------------------------------
   pwm_b: PROCESS (pwm_clk_b, ani2)
   BEGIN
      IF (pwm_clk_b = '0') THEN
         phase_b_pwm <= '1';
      ELSE
         phase_b_pwm <= ani2;
      END IF;
   END PROCESS pwm_b;   
   
   --------------------------------------------------------------------------
   -- DRIVE ENABLE OUTPUT SIGNALS PROCESS
   --------------------------------------------------------------------------
   drive_enable: PROCESS (reset, enable)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active                 
         ndea <= '0';                                 -- ensure drive A off
         ndeb <= '0';                                 -- ensure drive B off
         enable_led <= '0';                           -- enable led off
      ELSE                                            -- not in reset
         ndea <= enable;                              -- follow enable signal
         ndeb <= enable;                              -- follow enable signal 
         enable_led <= enable;                        -- follow enable signal
      END IF;
   END PROCESS drive_enable;
   
   --------------------------------------------------------------------------
   -- PHASE A OUTPUT SIGNALS PROCESS
   --------------------------------------------------------------------------
   phase_a_signals: PROCESS (position, phase_a_pwm)
   BEGIN
      IF (position(8) = '0') THEN                     -- in quad 1 or quad 2
         drv2a <= '1';                                -- hold signal high
         drv1a <= phase_a_pwm;                        -- reflect pwm signal
      ELSE                                            -- in quad 3 or quad 4
         drv2a <= phase_a_pwm;                        -- reflect pwm signal
         drv1a <= '1';                                -- hold signal high
      END IF;
   END PROCESS phase_a_signals;

   --------------------------------------------------------------------------
   -- PHASE B OUTPUT SIGNALS PROCESS
   --------------------------------------------------------------------------
   phase_b_signals: PROCESS (position, phase_b_pwm)
   BEGIN
      IF ((position(8) XOR position(7)) = '0') THEN   -- in quad 1 or quad 4                 
         drv2b <= '1';                                -- hold signal high
         drv1b <= phase_b_pwm;                        -- reflect pwm signal
      ELSE                                            -- in quad 2 or quad 3
         drv2b <= phase_b_pwm;                        -- reflect pwm signal
         drv1b <= '1';                                -- hold signal high
      END IF;
   END PROCESS phase_b_signals;   

   --------------------------------------------------------------------------
   -- SINE TABLE DATA PROCESS
   --------------------------------------------------------------------------
   sine_table: PROCESS (tablepos)
   BEGIN
      CASE tablepos IS
         WHEN 0 => sinetab <= "00000000";     -- 00H
         WHEN 1 => sinetab <= "00000011";     -- 03H
         WHEN 2 => sinetab <= "00000110";     -- 06H
         WHEN 3 => sinetab <= "00001001";     -- 09H
         WHEN 4 => sinetab <= "00001101";     -- 0DH
         WHEN 5 => sinetab <= "00010000";     -- 10H
         WHEN 6 => sinetab <= "00010011";     -- 13H
         WHEN 7 => sinetab <= "00010110";     -- 16H
         WHEN 8 => sinetab <= "00011001";     -- 19H
         WHEN 9 => sinetab <= "00011100";     -- 1CH
         WHEN 10 => sinetab <= "00011111";    -- 1FH
         WHEN 11 => sinetab <= "00100011";    -- 23H
         WHEN 12 => sinetab <= "00100110";    -- 26H
         WHEN 13 => sinetab <= "00101001";    -- 29H
         WHEN 14 => sinetab <= "00101100";    -- 2CH
         WHEN 15 => sinetab <= "00101111";    -- 2FH
         WHEN 16 => sinetab <= "00110010";    -- 32H
         WHEN 17 => sinetab <= "00110101";    -- 35H
         WHEN 18 => sinetab <= "00111000";    -- 38H
         WHEN 19 => sinetab <= "00111011";    -- 3BH
         WHEN 20 => sinetab <= "00111110";    -- 3EH
         WHEN 21 => sinetab <= "01000001";    -- 41H
         WHEN 22 => sinetab <= "01000101";    -- 45H
         WHEN 23 => sinetab <= "01001000";    -- 48H
         WHEN 24 => sinetab <= "01001011";    -- 4BH
         WHEN 25 => sinetab <= "01001110";    -- 4EH
         WHEN 26 => sinetab <= "01010001";    -- 51H
         WHEN 27 => sinetab <= "01010100";    -- 54H
         WHEN 28 => sinetab <= "01010111";    -- 57H
         WHEN 29 => sinetab <= "01011010";    -- 5AH
         WHEN 30 => sinetab <= "01011100";    -- 5CH
         WHEN 31 => sinetab <= "01011111";    -- 5FH
         WHEN 32 => sinetab <= "01100010";    -- 62H
         WHEN 33 => sinetab <= "01100101";    -- 65H
         WHEN 34 => sinetab <= "01101000";    -- 68H
         WHEN 35 => sinetab <= "01101011";    -- 6BH
         WHEN 36 => sinetab <= "01101110";    -- 6EH
         WHEN 37 => sinetab <= "01110001";    -- 71H
         WHEN 38 => sinetab <= "01110011";    -- 73H
         WHEN 39 => sinetab <= "01110110";    -- 76H
         WHEN 40 => sinetab <= "01111001";    -- 79H
         WHEN 41 => sinetab <= "01111100";    -- 7CH
         WHEN 42 => sinetab <= "01111111";    -- 7FH
         WHEN 43 => sinetab <= "10000001";    -- 81H
         WHEN 44 => sinetab <= "10000100";    -- 84H
         WHEN 45 => sinetab <= "10000111";    -- 87H
         WHEN 46 => sinetab <= "10001001";    -- 89H
         WHEN 47 => sinetab <= "10001100";    -- 8CH
         WHEN 48 => sinetab <= "10001111";    -- 8FH
         WHEN 49 => sinetab <= "10010001";    -- 91H
         WHEN 50 => sinetab <= "10010100";    -- 94H
         WHEN 51 => sinetab <= "10010110";    -- 96H
         WHEN 52 => sinetab <= "10011001";    -- 99H
         WHEN 53 => sinetab <= "10011011";    -- 9BH
         WHEN 54 => sinetab <= "10011110";    -- 9EH
         WHEN 55 => sinetab <= "10100000";    -- A0H
         WHEN 56 => sinetab <= "10100011";    -- A3H
         WHEN 57 => sinetab <= "10100101";    -- A5H
         WHEN 58 => sinetab <= "10101000";    -- A8H
         WHEN 59 => sinetab <= "10101010";    -- AAH
         WHEN 60 => sinetab <= "10101100";    -- ACH
         WHEN 61 => sinetab <= "10101111";    -- AFH
         WHEN 62 => sinetab <= "10110001";    -- B1H
         WHEN 63 => sinetab <= "10110011";    -- B3H
         WHEN 64 => sinetab <= "10110101";    -- B5H
         WHEN 65 => sinetab <= "10111000";    -- B8H
         WHEN 66 => sinetab <= "10111010";    -- BAH
         WHEN 67 => sinetab <= "10111100";    -- BCH
         WHEN 68 => sinetab <= "10111110";    -- BEH
         WHEN 69 => sinetab <= "11000000";    -- C0H
         WHEN 70 => sinetab <= "11000010";    -- C2H
         WHEN 71 => sinetab <= "11000100";    -- C4H
         WHEN 72 => sinetab <= "11000110";    -- C6H
         WHEN 73 => sinetab <= "11001000";    -- C8H
         WHEN 74 => sinetab <= "11001010";    -- CAH
         WHEN 75 => sinetab <= "11001100";    -- CCH
         WHEN 76 => sinetab <= "11001110";    -- CEH
         WHEN 77 => sinetab <= "11010000";    -- D0H
         WHEN 78 => sinetab <= "11010010";    -- D2H
         WHEN 79 => sinetab <= "11010011";    -- D3H
         WHEN 80 => sinetab <= "11010101";    -- D5H
         WHEN 81 => sinetab <= "11010111";    -- D7H
         WHEN 82 => sinetab <= "11011001";    -- D9H
         WHEN 83 => sinetab <= "11011010";    -- DAH
         WHEN 84 => sinetab <= "11011100";    -- DCH
         WHEN 85 => sinetab <= "11011101";    -- DDH
         WHEN 86 => sinetab <= "11011111";    -- DFH
         WHEN 87 => sinetab <= "11100000";    -- E0H
         WHEN 88 => sinetab <= "11100010";    -- E2H
         WHEN 89 => sinetab <= "11100011";    -- E3H
         WHEN 90 => sinetab <= "11100101";    -- E5H
         WHEN 91 => sinetab <= "11100110";    -- E6H
         WHEN 92 => sinetab <= "11100111";    -- E7H
         WHEN 93 => sinetab <= "11101001";    -- E9H
         WHEN 94 => sinetab <= "11101010";    -- EAH
         WHEN 95 => sinetab <= "11101011";    -- EBH
         WHEN 96 => sinetab <= "11101100";    -- ECH
         WHEN 97 => sinetab <= "11101110";    -- EEH
         WHEN 98 => sinetab <= "11101111";    -- EFH
         WHEN 99 => sinetab <= "11110000";    -- F0H
         WHEN 100 => sinetab <= "11110001";   -- F1H
         WHEN 101 => sinetab <= "11110010";   -- F2H
         WHEN 102 => sinetab <= "11110011";   -- F3H
         WHEN 103 => sinetab <= "11110100";   -- F4H
         WHEN 104 => sinetab <= "11110101";   -- F5H
         WHEN 105 => sinetab <= "11110110";   -- F6H
         WHEN 106 => sinetab <= "11110110";   -- F6H
         WHEN 107 => sinetab <= "11110111";   -- F7H
         WHEN 108 => sinetab <= "11111000";   -- F8H
         WHEN 109 => sinetab <= "11111001";   -- F9H
         WHEN 110 => sinetab <= "11111001";   -- F9H
         WHEN 111 => sinetab <= "11111010";   -- FAH
         WHEN 112 => sinetab <= "11111011";   -- FBH
         WHEN 113 => sinetab <= "11111011";   -- FBH
         WHEN 114 => sinetab <= "11111100";   -- FCH
         WHEN 115 => sinetab <= "11111100";   -- FCH
         WHEN 116 => sinetab <= "11111101";   -- FDH
         WHEN 117 => sinetab <= "11111101";   -- FDH
         WHEN 118 => sinetab <= "11111101";   -- FDH
         WHEN 119 => sinetab <= "11111110";   -- FEH
         WHEN 120 => sinetab <= "11111110";   -- FEH
         WHEN 121 => sinetab <= "11111110";   -- FEH
         WHEN 122 => sinetab <= "11111111";   -- FFH
         WHEN 123 => sinetab <= "11111111";   -- FFH
         WHEN 124 => sinetab <= "11111111";   -- FFH
         WHEN 125 => sinetab <= "11111111";   -- FFH
         WHEN 126 => sinetab <= "11111111";   -- FFH
         WHEN 127 => sinetab <= "11111111";   -- FFH
      END CASE;
   END PROCESS sine_table;

   --------------------------------------------------------------------------
   -- TABLE SELECTOR PROCESS
   --------------------------------------------------------------------------
   table_sel: PROCESS (pres_state, position, tablepos)
   VARIABLE shortpos: STD_LOGIC_VECTOR (6 downto 0);
   VARIABLE sinepos: STD_LOGIC_VECTOR (6 downto 0);
   VARIABLE cosinepos: STD_LOGIC_VECTOR (6 downto 0);
   BEGIN
      shortpos := position(6 downto 0);               -- use last bits of position      
      IF (position(7) = '0') THEN                     -- quadrant 1 or quadrant 3
         sinepos := shortpos;                         -- use short position
      ELSE
         sinepos := 127 - shortpos;                   -- invert short position
      END IF;
      IF (position(7) = '1') THEN                     -- quadrant 2 or quadrant 4
         cosinepos := shortpos;                       -- use short position
      ELSE
         cosinepos := 127 - shortpos;                 -- invert short position
      END IF;
      CASE pres_state IS
         WHEN st0 => tablepos <= 0;                    -- return zero
         WHEN st1 => tablepos <= conv_integer(sinepos);-- take sine value
         WHEN st2 => tablepos <= conv_integer(cosinepos);-- take cosine value
      END CASE;
   END PROCESS table_sel;

   --------------------------------------------------------------------------
   -- STATE MACHINE PROCESS
   --------------------------------------------------------------------------
   fsm: PROCESS (reset, clk, pres_state, position)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active
         pres_state <= st0;                           -- set initial state
         lastposition <= (OTHERS => '0');             -- zero last position
         sine <= (OTHERS => '0');                     -- zero sine value
         cosine <= (OTHERS => '1');                   -- cosine to max value
      ELSIF rising_edge(clk) THEN                      -- positive clock edge
         CASE pres_state IS
            WHEN st0 =>                               -- STATE 0
               IF (position = lastposition) THEN      -- compare positions
                  pres_state <= st0;                  -- wait for move
               ELSE
                  lastposition <= position;           -- update last position
                  pres_state <= st1;                  -- move to next state                
               END IF;
            WHEN st1 =>                               -- STATE 1
               sine <= sinetab;                       -- latch sine result 
               pres_state <= st2;                     -- move to next state               
            WHEN st2 =>                               -- STATE 2 
               cosine <= sinetab;                     -- latch cosine result
               pres_state <= st0;                     -- move to initial state
         END CASE;
      END IF;
   END PROCESS fsm;

   --------------------------------------------------------------------------
   -- STEP PROCESS
   --------------------------------------------------------------------------
   dostep: PROCESS (reset, step, dir, stepsize, position)
   BEGIN
      IF (reset = '0') THEN                          -- reset is active
         temppos <= (OTHERS => '0');                 -- zero temp position
         step_led <= '0';                            -- step led off
      ELSIF falling_edge(step) THEN                  -- we are to step
         step_led <= NOT step_led;                   -- invert step led
         IF (dir = '1') THEN                         -- check direction
            temppos <= position + stepsize;          -- move forward
         ELSE
            temppos <= position - stepsize;          -- move backward
         END IF;
      END IF;
   END PROCESS dostep;

   --------------------------------------------------------------------------
   -- STEP POSITION PROCESS
   --------------------------------------------------------------------------
   step_position: PROCESS (reset, temppos, stepmask)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active
         position <= (OTHERS => '0');                 -- zero position
      ELSE
         position <= temppos AND stepmask;            -- mask off bits       
   END IF;
   END PROCESS step_position;

   --------------------------------------------------------------------------
   -- RESOLUTION SWITCH SETTINGS PROCESS
   --------------------------------------------------------------------------
   res_select: PROCESS (sw)
   BEGIN
      CASE sw IS
         WHEN "1101" => stepsize <= 1;                -- 250 microsteps per step
         WHEN "1100" => stepsize <= 2;                -- 125 microsteps per step
         WHEN "1011" => stepsize <= 5;                -- 50 microsteps per step
         WHEN "1010" => stepsize <= 10;               -- 25 microsteps per step
         WHEN "1001" => stepsize <= 25;               -- 10 microsteps per step
         WHEN "1000" => stepsize <= 50;               -- 5 microsteps per step
         WHEN "0111" => stepsize <= 1;                -- 256 microsteps per step
         WHEN "0110" => stepsize <= 2;                -- 128 microsteps per step
         WHEN "0101" => stepsize <= 4;                -- 64 microsteps per step
         WHEN "0100" => stepsize <= 8;                -- 32 microsteps per step
         WHEN "0011" => stepsize <= 16;               -- 16 microsteps per step
         WHEN "0010" => stepsize <= 32;               -- 8 microsteps per step
         WHEN "0001" => stepsize <= 64;               -- 4 microsteps per step
         WHEN "0000" => stepsize <= 128;              -- 2 microsteps per step
         WHEN OTHERS => stepsize <= 16;               -- good coding practice
      END CASE;
   END PROCESS res_select;

   --------------------------------------------------------------------------
   -- MASK FOR STEP SIZE PROCESS
   --------------------------------------------------------------------------
   stepsize_mask: PROCESS (reset, stepsize)
   BEGIN
      IF (reset = '0') THEN                           -- reset is active
         stepmask <= (OTHERS => '1');                 -- set all mask bits
      ELSE 
         stepmask <= NOT CONV_STD_LOGIC_VECTOR(stepsize-1, 9);
      END IF;
   END PROCESS stepsize_mask;

END behaviour;

Very interesting. That is an efficient driver at a glance, will have to look at it a little more closely. Thank you for that.

While I would be interested in the version with power boost, I would not ask for any extra effort, you have already been very helpful.
ARM BASIC: For the love of Simplicity, Fast Interpreted BASIC, and Assembly Language.
Always KISS Keep It Simple Silly.
User avatar
Posts: 2804
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by LdB » Fri Mar 17, 2017 4:01 am
No worries I will explain how to do the boost because it's easy you obviously understand VHDL

1.) Each time the unit pulse it adds 1 to a boost counter, we used 100 as max (being a rough percentage)
2.) On a fixed boost rate counter (usually like 500Hz) you subtract 1 from the boost counter

So the boost counter will be zero for any frequency below your boost clock rate and will be some larger value (up to max) the larger the pulse rate is above your boost rate clock. If pulses stop the boost number quickly drops back to zero (1/5th of a second because you have 500 boost clocks subtracting 1). If you think about it if the pulse rate was like 501Hz the boost would ever so slowly get applied (100 seconds) but at 1000hz it gets applied very quickly (1/10th of a second) which is how you want it.

Essentially you extend the sine/cosine table to 9 bits and add the scaled boost as a base offset to the sine/cosine DAC outputs.

So it essentially jacks the PWM trigger levels up based on the pulse rate the unit is seeing. It will apply the boost very quickly as the pulse rate is increasing in ramp and will drop the boost off in 1/5th of a second if stepping stops so you don't burn the motor out.

That is all there really is too it, you get more power only when the motor is rotating at speed and the power normally drops off because of the back EMF.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by jojopi » Fri Mar 17, 2017 8:22 am
LdB wrote:There are many Posix compliant embedded systems where I basically get numbers 1-80 without installing a locale which is also pretty much what a raw USB HID gives you being 1-231.
You are missing my point, and applying knowledge from a very different situation. This thread was about reading input from the controlling terminal without blocking or line-buffering. Every locale supported in Raspbian uses a character encoding that is ASCII-compatible in the first 128 byte values.

Regardless of keyboard type, layout, locale, and whatever ncurses might default to in the absence of locale setup, when the user types ASCII characters like +, -, /, the application will read ASCII codes from /dev/tty. You do not have to worry about character encoding mismatches until you need to read non-ASCII glyphs, and DavidS' keyboard probably does not have any of those.

You seem to be thinking of communicating with hardware devices directly, not through the terminal interface. Incidentally, in Linux even when you read keyboard scan codes such as via /dev/input/, you are not seeing the real USB HID codes but their translation to Linux internal codes which are compatible across all supported platforms.
User avatar
Posts: 2929
Joined: Tue Oct 11, 2011 8:38 pm
by LdB » Sat Mar 18, 2017 11:22 am
jojopi wrote: Every locale supported in Raspbian uses a character encoding that is ASCII-compatible in the first 128 byte values.

Regardless of keyboard type, layout, locale, and whatever ncurses might default to in the absence of locale setup, when the user types ASCII characters like +, -, /, the application will read ASCII codes from /dev/tty. You do not have to worry about character encoding mismatches until you need to read non-ASCII glyphs, and DavidS' keyboard probably does not have any of those.

I get what you are saying that linux has some standard which you are saying will enforce a standard on C on the console. I don't know or care enough about the linux console to look it up but there would be an obvious consequence if what you said is true. You can't use any other language except english on a linux console which I found rather surprising. Very few foreign languages can be encode into the bottom 127 characters of an ascii set if you have to have all the English ones there.

I guess the other choice is all foreign languages must encode in the high 127 characters which seems a bit heavy handed by the English speaking world to essentially reserve half the keyboard space for letters that will never be used.

That is a good way to stop 7/10ths of the world population ever being able to use or write a console program in linux .. way to go who came up with that great idea (you can bet they speak English). Chinese, most of Asia and Russians must love Linux it's more draconian than Windows or DOS ever were.

I have written for KDE and it uses UTF8 so that means there is a secondary encoding in the graphical interface which I never realized .. so you learn something everyday.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by jojopi » Sun Mar 19, 2017 3:43 pm
LdB wrote:That is a good way to stop 7/10ths of the world population ever being able to use or write a console program in linux ..
Can you not see that it is quite the opposite? In your scheme where we could not rely on ASCII characters being invariant across encodings, nobody using a non-Latin native script would be able to type "ls" or "#include <stdio.h>", or read any code or output from an unlocalized program. Chaos!
I have written for KDE and it uses UTF8 so that means there is a secondary encoding in the graphical interface which I never realized ..
UTF-8 is the most common encoding in Raspbian locales. It is the default everywhere, not just in the GUI. It is ASCII-compatible, and it supports mixing any combination of scripts in a single file.
User avatar
Posts: 2929
Joined: Tue Oct 11, 2011 8:38 pm
by jahboater » Sun Mar 19, 2017 4:39 pm
LdB wrote:I have written for KDE and it uses UTF8 so that means there is a secondary encoding in the graphical interface which I never realized .. so you learn something everyday.
You have probably been using it all along:
Code: Select all
pi@raspberrypi:~ $ echo $LANG
en_GB.UTF-8
Posts: 1140
Joined: Wed Feb 04, 2015 6:38 pm
by LdB » Sun Mar 19, 2017 7:40 pm
jojopi wrote:
LdB wrote:That is a good way to stop 7/10ths of the world population ever being able to use or write a console program in linux ..
Can you not see that it is quite the opposite? In your scheme where we could not rely on ASCII characters being invariant across encodings, nobody using a non-Latin native script would be able to type "ls" or "#include <stdio.h>", or read any code or output from an unlocalized program. Chaos!

You aren't thinking clearly and your answer is very I only use English centric ;-)

What you just stated is you are FORCING them to use english .. you want them to type "LS".

Why should they have to type "LS" ... why can't they type two of there own symbols. It's only an encoding if the command interpreter runs the same symbol translation you get the same result as when you type LS but you don't have to type those particular letters"LS". That is what supporting a foreign language is all about.

To bang the point home to you, I could ask you what you do if your language and hence your keyboard doesn't have the letters "L" or "S" on it :-)

I leave out the obvious extension that you limited them to creating only English filenames, which is funny because they can create UTF8 filenames on the GUI. However you can't read or display those UTF8 names on your console app either. If you think you can how do you plan on typing the name in to open them.

So it's not a solution, it's a nightmare for them.

I get what linux is doing but it's horrible and sorry you can't paint that as a pretty picture .. it only works for English speakers. Doing some reading the whole background to this looks like it based around the mailer exchanges being only allowed to use 7 bits, funny stories of trying to get foreign languages thru the mailers. Linux is sort of in the same boat Windows got itself in until it bite the bullet with full language support.

There is no chaos in running language translation for programs including O/S you just have to do it properly from the bottom up.

See even in C this works perfectly but it looks funny to you doesn't it. You will note the keywords can't be changed on this particular C compiler on some you can put the keywords into your own language depends if it has proper language support or not. Hopefully you see that even when I turn the keywords in C into a different language that you can't read doesn't stop it from working just you can't read it. So your #include in your discussion above is misplaced as C is not confined to English in any way.
Code: Select all
#define 整型    int
#define 输出    printf
#define 面函数  main
#define 返回    return
#define 定义    typedef
#define 不可变  const
#define 字符    char
#define 指针    *
#define 为

定义 不可变 字符 指针 为 字面值;

整型 面函数() {
  字面值 蛤蛤 = "\u82df\u5229\u56fd\u5bb6\u751f\u6b7b\u4ee5\uff0c"
               "\u5c82\u56e0\u7978\u798f\u907f\u8d8b\u4e4b";
  输出("%s\n", 蛤蛤);
返回 0;

Anyhow I get it but it isn't pretty and a bit of a mess on console apps.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by jojopi » Mon Mar 20, 2017 6:07 pm
LdB wrote:#define 整型 int
You have included two different scripts in the same file, using UTF-8; exactly the way that Raspbian supports. So now I really do not understand what you think is "not a solution", "a nightmare", "horrible", or "only works for English speakers".

I gather you do not actually use Linux, so perhaps I have misled you in some way. Yes you can use UTF-8 in file names too, and access them from a terminal, subject to having the necessary fonts available. (Tab completion and wild cards are useful if you are accessing someone else's files and do not want to learn their input methods.)

"ls" is not really English, it is the name of a command. You may call it something else if you prefer. My point is that, if setting LANG=zh meant that you could only run ls as "编目", or if it changed the way file names were encoded on disk, then a Chinese user could not run a shell script written by someone from a different locale. Same problem with including one person's .h in another's .c, if the encodings were incompatible. I do not believe that any of this is specific to Unix systems.
User avatar
Posts: 2929
Joined: Tue Oct 11, 2011 8:38 pm
by LdB » Mon Mar 20, 2017 7:08 pm
Go back to the solution for David .... You can't punch UTF8 thru those C calls it won't work.

So what we have established is :
Linux console supports UTF8 if I select the right font on the console which is great we have part A solved we can display the UTF8 characters.
On Windows and KDE, I have an API to get the UTF8/16 which is the bit we are missing on the console.

So how do I get the UTF8 input into a C console program and you help me and answer Davids problem in full for all languages?

Windows does it now by exposing the API to the console (so you can fetch UTF16) which you run an extern directive to in C, and I would think Linux would probably have to do it the same way. The fact they have put UTF8 display out on the console makes me hopeful they have done the other bit and that is the proper answer to this problem. So you also say you can type in UTF8 on the command shell (with the right font) which I think means the UTF8 API is exposed to the console I just need to tell C how to get to it.

You have instilled me with hope that linux like windows has finally fixed this. I am not arguing with you anymore I want to know how to do it. I use Windows rather than Linux based on this one thing as many of my programmers aren't English and some of their filenames can't be accessed from the command line.
Posts: 136
Joined: Wed Dec 07, 2016 2:29 pm
by jojopi » Mon Mar 20, 2017 11:41 pm
LdB wrote:Go back to the solution for David .... You can't punch UTF8 thru those C calls it won't work.
UTF-8 will go through the terminal fine; it is just bytes. In many cases you do not even need to know. For instance, you prompt the user for a file name. The user types or pastes something. You know the input is complete when you see a newline. You replace the newline with a nul byte and pass the string to open(). This opens the correct file.

When you do need to understand the text you are handling, there are basically two approaches. You can keep everything as UTF-8 and deal with the fact that the number of bytes may not equal the number of characters. Or you can use UTF-8 for input and output, but convert to wchar_t arrays for internal storage and processing. In GNU systems, wchar_t is always UCS-4 (UTF-32, even if the locale's byte encoding is not UTF-8).

C90 and C99 have tons of options for converting between mbr and wcs formats, including input and output functions like getwchar() that will work directly on a terminal. There is also ncursesw (for some reason a completely separate version of the library), which provides get_wch().
User avatar
Posts: 2929
Joined: Tue Oct 11, 2011 8:38 pm